Merge remote-tracking branch 'origin/main' into ds-apiserver-with-configs
This commit is contained in:
+53
-43
@@ -134,7 +134,7 @@ To integrate your OAuth2 provider with Grafana using our Generic OAuth authentic
|
||||
|
||||
### Configure login
|
||||
|
||||
Grafana can resolve a user's login from the OAuth2 ID token or user information retrieved from the OAuth2 UserInfo endpoint.
|
||||
Grafana can resolve a user's login from the OAuth2 ID token, user information retrieved from the OAuth2 UserInfo endpoint, or the OAuth2 access token.
|
||||
Grafana looks at these sources in the order listed until it finds a login.
|
||||
If no login is found, then the user's login is set to user's email address.
|
||||
|
||||
@@ -146,10 +146,12 @@ Refer to the following table for information on what to configure based on how y
|
||||
| Another field of the OAuth2 ID token. | Set `login_attribute_path` configuration option. |
|
||||
| `login` or `username` field of the user information from the UserInfo endpoint. | N/A |
|
||||
| Another field of the user information from the UserInfo endpoint. | Set `login_attribute_path` configuration option. |
|
||||
| `login` or `username` field of the OAuth2 access token. | N/A |
|
||||
| Another field of the OAuth2 access token. | Set `login_attribute_path` configuration option. |
|
||||
|
||||
### Configure display name
|
||||
|
||||
Grafana can resolve a user's display name from the OAuth2 ID token or user information retrieved from the OAuth2 UserInfo endpoint.
|
||||
Grafana can resolve a user's display name from the OAuth2 ID token, user information retrieved from the OAuth2 UserInfo endpoint, or the OAuth2 access token.
|
||||
Grafana looks at these sources in the order listed until it finds a display name.
|
||||
If no display name is found, then user's login is displayed instead.
|
||||
|
||||
@@ -161,10 +163,12 @@ Refer to the following table for information on what you need to configure depen
|
||||
| Another field of the OAuth2 ID token. | Set `name_attribute_path` configuration option. |
|
||||
| `name` or `display_name` field of the user information from the UserInfo endpoint. | N/A |
|
||||
| Another field of the user information from the UserInfo endpoint. | Set `name_attribute_path` configuration option. |
|
||||
| `name` or `display_name` field of the OAuth2 access token. | N/A |
|
||||
| Another field of the OAuth2 access token. | Set `name_attribute_path` configuration option. |
|
||||
|
||||
### Configure email address
|
||||
|
||||
Grafana can resolve the user's email address from the OAuth2 ID token, the user information retrieved from the OAuth2 UserInfo endpoint, or the OAuth2 `/emails` endpoint.
|
||||
Grafana can resolve the user's email address from the OAuth2 ID token, the user information retrieved from the OAuth2 UserInfo endpoint, the OAuth2 access token, or the OAuth2 `/emails` endpoint.
|
||||
Grafana looks at these sources in the order listed until an email address is found.
|
||||
If no email is found, then the email address of the user is set to an empty string.
|
||||
|
||||
@@ -177,6 +181,10 @@ Refer to the following table for information on what to configure based on how t
|
||||
| `upn` field of the OAuth2 ID token. | N/A |
|
||||
| `email` field of the user information from the UserInfo endpoint. | N/A |
|
||||
| Another field of the user information from the UserInfo endpoint. | Set `email_attribute_path` configuration option. |
|
||||
| `email` field of the OAuth2 access token. | N/A |
|
||||
| `attributes` map of the OAuth2 access token. | Set `email_attribute_name` configuration option. By default, Grafana searches for email under `email:primary` key. |
|
||||
| `upn` field of the OAuth2 access token. | N/A |
|
||||
| Another field of the OAuth2 access token. | Set `email_attribute_path` configuration option. |
|
||||
| Email address marked as primary from the `/emails` endpoint of <br /> the OAuth2 provider (obtained by appending `/emails` to the URL <br /> configured with `api_url`) | N/A |
|
||||
|
||||
### Configure a refresh token
|
||||
@@ -199,6 +207,7 @@ The `accessTokenExpirationCheck` feature toggle has been removed in Grafana v10.
|
||||
Unless `skip_org_role_sync` option is enabled, the user's role will be set to the role retrieved from the auth provider upon user login.
|
||||
|
||||
The user's role is retrieved using a [JMESPath](http://jmespath.org/examples.html) expression from the `role_attribute_path` configuration option.
|
||||
Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. If still no role is found, the expression will be evaluated using the OAuth2 access token.
|
||||
To map the server administrator role, use the `allow_assign_grafana_admin` configuration option.
|
||||
Refer to [configuration options](#configuration-options) for more information.
|
||||
|
||||
@@ -326,6 +335,7 @@ By using Team Sync, you can link your OAuth2 groups to teams within Grafana. Thi
|
||||
Teams for each user are synchronized when the user logs in.
|
||||
|
||||
Generic OAuth groups can be referenced by group ID, such as `8bab1c86-8fba-33e5-2089-1d1c80ec267d` or `myteam`.
|
||||
Group information can be extracted from the OAuth2 ID token, user information from the UserInfo endpoint, or the OAuth2 access token.
|
||||
For information on configuring OAuth2 groups with Grafana using the `groups_attribute_path` configuration option, refer to [configuration options](#configuration-options).
|
||||
|
||||
To learn more about Team Sync, refer to [Configure team sync](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-team-sync/).
|
||||
@@ -359,46 +369,46 @@ The following table outlines the various Generic OAuth configuration options. Yo
|
||||
If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"`
|
||||
{{< /admonition >}}
|
||||
|
||||
| Setting | Required | Supported on Cloud | Description | Default |
|
||||
| ---------------------------- | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
|
||||
| `enabled` | No | Yes | Enables Generic OAuth authentication. | `false` |
|
||||
| `name` | No | Yes | Name that refers to the Generic OAuth authentication from the Grafana user interface. | `OAuth` |
|
||||
| `icon` | No | Yes | Icon used for the Generic OAuth authentication in the Grafana user interface. | `signin` |
|
||||
| `client_id` | Yes | Yes | Client ID provided by your OAuth2 app. | |
|
||||
| `client_secret` | Yes | Yes | Client secret provided by your OAuth2 app. | |
|
||||
| `auth_url` | Yes | Yes | Authorization endpoint of your OAuth2 provider. | |
|
||||
| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | |
|
||||
| `api_url` | Yes | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | |
|
||||
| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` |
|
||||
| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `user:email` |
|
||||
| `empty_scopes` | No | Yes | Set to `true` to use an empty scope during authentication. | `false` |
|
||||
| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Generic OAuth login. Only existing Grafana users can log in with Generic OAuth if set to `false`. | `true` |
|
||||
| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` |
|
||||
| `id_token_attribute_name` | No | Yes | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` |
|
||||
| `login_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login](#configure-login). | |
|
||||
| `name_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name](#configure-display-name). | |
|
||||
| `email_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address](#configure-email-address). | |
|
||||
| `email_attribute_name` | No | Yes | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address](#configure-email-address). | `email:primary` |
|
||||
| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | |
|
||||
| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | `false` |
|
||||
| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` |
|
||||
| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
|
||||
| `org_mapping` | No | No | List of comma- or space-separated `<ExternalOrgName>:<OrgIdOrName>:<Role>` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
|
||||
| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | `false` |
|
||||
| `groups_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | |
|
||||
| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | |
|
||||
| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | |
|
||||
| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | |
|
||||
| `team_ids` | No | Yes | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | |
|
||||
| `team_ids_attribute_path` | No | Yes | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | |
|
||||
| `teams_url` | No | Yes | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | |
|
||||
| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` |
|
||||
| `tls_client_cert` | No | No | The path to the certificate. | |
|
||||
| `tls_client_key` | No | No | The path to the key. | |
|
||||
| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | |
|
||||
| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` |
|
||||
| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` |
|
||||
| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | |
|
||||
| Setting | Required | Supported on Cloud | Description | Default |
|
||||
| ---------------------------- | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
|
||||
| `enabled` | No | Yes | Enables Generic OAuth authentication. | `false` |
|
||||
| `name` | No | Yes | Name that refers to the Generic OAuth authentication from the Grafana user interface. | `OAuth` |
|
||||
| `icon` | No | Yes | Icon used for the Generic OAuth authentication in the Grafana user interface. | `signin` |
|
||||
| `client_id` | Yes | Yes | Client ID provided by your OAuth2 app. | |
|
||||
| `client_secret` | Yes | Yes | Client secret provided by your OAuth2 app. | |
|
||||
| `auth_url` | Yes | Yes | Authorization endpoint of your OAuth2 provider. | |
|
||||
| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | |
|
||||
| `api_url` | Yes | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | |
|
||||
| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` |
|
||||
| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `user:email` |
|
||||
| `empty_scopes` | No | Yes | Set to `true` to use an empty scope during authentication. | `false` |
|
||||
| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Generic OAuth login. Only existing Grafana users can log in with Generic OAuth if set to `false`. | `true` |
|
||||
| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` |
|
||||
| `id_token_attribute_name` | No | Yes | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` |
|
||||
| `login_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login](#configure-login). | |
|
||||
| `name_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name](#configure-display-name). | |
|
||||
| `email_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address](#configure-email-address). | |
|
||||
| `email_attribute_name` | No | Yes | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address](#configure-email-address). | `email:primary` |
|
||||
| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. If still no role is found, the expression will be evaluated using the OAuth2 access token. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | |
|
||||
| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | `false` |
|
||||
| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` |
|
||||
| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. If still no value is returned, the expression will be evaluated using the OAuth2 access token. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
|
||||
| `org_mapping` | No | No | List of comma- or space-separated `<ExternalOrgName>:<OrgIdOrName>:<Role>` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
|
||||
| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping](#configure-role-mapping). | `false` |
|
||||
| `groups_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. If still no groups are found, the expression will be evaluated using the OAuth2 access token. The result of the evaluation should be a string array of groups. | |
|
||||
| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | |
|
||||
| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | |
|
||||
| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | |
|
||||
| `team_ids` | No | Yes | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | |
|
||||
| `team_ids_attribute_path` | No | Yes | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | |
|
||||
| `teams_url` | No | Yes | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | |
|
||||
| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` |
|
||||
| `tls_client_cert` | No | No | The path to the certificate. | |
|
||||
| `tls_client_key` | No | No | The path to the key. | |
|
||||
| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | |
|
||||
| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` |
|
||||
| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` |
|
||||
| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | |
|
||||
|
||||
## Examples of setting up Generic OAuth
|
||||
|
||||
|
||||
+6
-6
@@ -111,7 +111,7 @@
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@types/babel__core": "^7",
|
||||
"@types/babel__preset-env": "^7",
|
||||
"@types/chance": "^1.1.3",
|
||||
"@types/chance": "^1.1.7",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/confusing-browser-globals": "^1",
|
||||
"@types/d3": "7.4.3",
|
||||
@@ -164,19 +164,19 @@
|
||||
"@types/yargs": "17.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "8.35.1",
|
||||
"@typescript-eslint/parser": "8.35.1",
|
||||
"autoprefixer": "10.4.20",
|
||||
"autoprefixer": "10.4.21",
|
||||
"babel-loader": "9.2.1",
|
||||
"blob-polyfill": "9.0.20240710",
|
||||
"browserslist": "^4.21.4",
|
||||
"chance": "^1.0.10",
|
||||
"chrome-remote-interface": "0.33.2",
|
||||
"chance": "^1.1.13",
|
||||
"chrome-remote-interface": "0.33.3",
|
||||
"codeowners": "^5.1.1",
|
||||
"confusing-browser-globals": "^1.0.11",
|
||||
"copy-webpack-plugin": "12.0.2",
|
||||
"core-js": "3.40.0",
|
||||
"crashme": "0.0.15",
|
||||
"css-loader": "7.1.2",
|
||||
"css-minimizer-webpack-plugin": "7.0.0",
|
||||
"css-minimizer-webpack-plugin": "7.0.2",
|
||||
"cypress": "14.3.2",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"cypress-recurse": "^1.35.3",
|
||||
@@ -281,7 +281,7 @@
|
||||
"@grafana/flamegraph": "workspace:*",
|
||||
"@grafana/google-sdk": "0.3.2",
|
||||
"@grafana/i18n": "workspace:*",
|
||||
"@grafana/lezer-logql": "0.2.7",
|
||||
"@grafana/lezer-logql": "0.2.8",
|
||||
"@grafana/llm": "0.22.1",
|
||||
"@grafana/monaco-logql": "^0.0.8",
|
||||
"@grafana/o11y-ds-frontend": "workspace:*",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PropsWithChildren, ReactElement, useMemo } from 'react';
|
||||
|
||||
import { DataSourceInstanceSettings } from '../../types/datasource';
|
||||
|
||||
import { Context, DataSourcePluginContextType } from './PluginContext';
|
||||
import { PluginContext, DataSourcePluginContextType } from './PluginContext';
|
||||
|
||||
export type DataSourcePluginContextProviderProps = {
|
||||
instanceSettings: DataSourceInstanceSettings;
|
||||
@@ -16,5 +16,5 @@ export function DataSourcePluginContextProvider(
|
||||
return { instanceSettings, meta: instanceSettings.meta };
|
||||
}, [instanceSettings]);
|
||||
|
||||
return <Context.Provider value={value}>{children}</Context.Provider>;
|
||||
return <PluginContext.Provider value={value}>{children}</PluginContext.Provider>;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { KeyValue } from '../../types/data';
|
||||
import { DataSourceInstanceSettings } from '../../types/datasource';
|
||||
import { PluginMeta } from '../../types/plugin';
|
||||
|
||||
export interface PluginContextType {
|
||||
meta: PluginMeta;
|
||||
export interface PluginContextType<T extends KeyValue = KeyValue> {
|
||||
meta: PluginMeta<T>;
|
||||
}
|
||||
|
||||
export interface DataSourcePluginContextType extends PluginContextType {
|
||||
export interface DataSourcePluginContextType<T extends KeyValue = KeyValue> extends PluginContextType<T> {
|
||||
instanceSettings: DataSourceInstanceSettings;
|
||||
}
|
||||
|
||||
export const Context = createContext<PluginContextType | undefined>(undefined);
|
||||
export const PluginContext = createContext<PluginContextType | undefined>(undefined);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { PluginMeta } from '../../types/plugin';
|
||||
|
||||
import { Context } from './PluginContext';
|
||||
import { PluginContext } from './PluginContext';
|
||||
|
||||
export type PluginContextProviderProps = {
|
||||
meta: PluginMeta;
|
||||
@@ -10,5 +10,5 @@ export type PluginContextProviderProps = {
|
||||
|
||||
export function PluginContextProvider(props: PropsWithChildren<PluginContextProviderProps>): ReactElement {
|
||||
const { children, ...rest } = props;
|
||||
return <Context.Provider value={rest}>{children}</Context.Provider>;
|
||||
return <PluginContext.Provider value={rest}>{children}</PluginContext.Provider>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { KeyValue } from '../../types/data';
|
||||
|
||||
import { type DataSourcePluginContextType, type PluginContextType } from './PluginContext';
|
||||
|
||||
export function isDataSourcePluginContext(context: PluginContextType): context is DataSourcePluginContextType {
|
||||
export function isDataSourcePluginContext<T extends KeyValue = KeyValue>(
|
||||
context: PluginContextType<T>
|
||||
): context is DataSourcePluginContextType<T> {
|
||||
return 'instanceSettings' in context && 'meta' in context;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { Context, PluginContextType } from './PluginContext';
|
||||
import { KeyValue } from '../../types/data';
|
||||
|
||||
export function usePluginContext(): PluginContextType | null {
|
||||
const context = useContext(Context);
|
||||
import { PluginContext, PluginContextType } from './PluginContext';
|
||||
|
||||
export function usePluginContext<T extends KeyValue = KeyValue>(): PluginContextType<T> | null {
|
||||
const context = useContext(PluginContext);
|
||||
|
||||
// The extensions hooks (e.g. `usePluginLinks()`) are using this hook to check
|
||||
// if they are inside a plugin or not (core Grafana), so we should be able to return an empty state as well (`null`).
|
||||
@@ -11,5 +13,6 @@ export function usePluginContext(): PluginContextType | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
return context;
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return context as PluginContextType<T>;
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ export { type GroupingToMatrixTransformerOptions } from './transformations/trans
|
||||
export {
|
||||
type PluginContextType,
|
||||
type DataSourcePluginContextType,
|
||||
Context as PluginContext,
|
||||
PluginContext,
|
||||
} from './context/plugins/PluginContext';
|
||||
export { type PluginContextProviderProps, PluginContextProvider } from './context/plugins/PluginContextProvider';
|
||||
export {
|
||||
|
||||
@@ -35,18 +35,21 @@ describe('Limit transformer', () => {
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
state: { calcs: undefined },
|
||||
values: [3000, 4000, 5000],
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: FieldType.string,
|
||||
state: { calcs: undefined },
|
||||
values: ['one', 'two', 'two'],
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'values',
|
||||
type: FieldType.number,
|
||||
state: { calcs: undefined },
|
||||
values: [1, 2, 2],
|
||||
config: {},
|
||||
},
|
||||
@@ -79,18 +82,21 @@ describe('Limit transformer', () => {
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
state: { calcs: undefined },
|
||||
values: [6000, 7000, 8000],
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: FieldType.string,
|
||||
state: { calcs: undefined },
|
||||
values: ['three', 'three', 'three'],
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'values',
|
||||
type: FieldType.number,
|
||||
state: { calcs: undefined },
|
||||
values: [3, 3, 3],
|
||||
config: {},
|
||||
},
|
||||
|
||||
@@ -37,6 +37,12 @@ export const limitTransformer: DataTransformerInfo<LimitTransformerOptions> = {
|
||||
fields: frame.fields.map((f) => {
|
||||
return {
|
||||
...f,
|
||||
// Clear cached field calculations since applying a limit changes the dataset
|
||||
// and previously computed stats (min, max, mean, etc.) are no longer valid
|
||||
state: {
|
||||
...f.state,
|
||||
calcs: undefined,
|
||||
},
|
||||
values:
|
||||
limit >= 0 ? f.values.slice(0, limit) : f.values.slice(f.values.length + limit, f.values.length),
|
||||
};
|
||||
|
||||
@@ -48,6 +48,14 @@ describe('fuzzySearch', () => {
|
||||
expect(result.map((idx) => haystack[idx])).toEqual(['A水']);
|
||||
});
|
||||
|
||||
it('should do case-insensitive substring match when needle contains non-ascii characters', () => {
|
||||
const haystack = ['Über'];
|
||||
const needle = 'ü';
|
||||
const result = fuzzySearch(haystack, needle);
|
||||
|
||||
expect(result.map((idx) => haystack[idx])).toEqual(['Über']);
|
||||
});
|
||||
|
||||
it('should handle multiple non-latin characters', () => {
|
||||
const haystack = ['台灣省', '台中市', '台北市', '台南市', '南投縣', '高雄市', '台中第一高級中學'];
|
||||
const needle = '南';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import uFuzzy from '@leeoniya/ufuzzy';
|
||||
|
||||
import { escapeRegex } from '../text/string';
|
||||
|
||||
// https://catonmat.net/my-favorite-regex :)
|
||||
const REGEXP_NON_ASCII = /[^ -~]/m;
|
||||
// https://www.asciitable.com/
|
||||
@@ -36,11 +38,13 @@ export function fuzzySearch(haystack: string[], needle: string): number[] {
|
||||
needle.length > maxNeedleLength ||
|
||||
uf.split(needle).length > maxFuzzyTerms
|
||||
) {
|
||||
const needleRegex = new RegExp(escapeRegex(needle), 'i');
|
||||
const indices: number[] = [];
|
||||
|
||||
for (let i = 0; i < haystack.length; i++) {
|
||||
let item = haystack[i];
|
||||
|
||||
if (item.includes(needle)) {
|
||||
if (needleRegex.test(item)) {
|
||||
indices.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,11 +1179,6 @@ export const versionedComponents = {
|
||||
'12.1.0': 'data-testid Data links actions tooltip wrapper',
|
||||
},
|
||||
},
|
||||
TablePanel: {
|
||||
autoCell: {
|
||||
'12.1.0': 'data-testid Table panel auto cell',
|
||||
},
|
||||
},
|
||||
CodeEditor: {
|
||||
container: {
|
||||
'10.2.3': 'data-testid Code editor container',
|
||||
|
||||
@@ -708,6 +708,7 @@ export enum TableCellDisplayMode {
|
||||
Image = 'image',
|
||||
JSONView = 'json-view',
|
||||
LcdGauge = 'lcd-gauge',
|
||||
Pill = 'pill',
|
||||
Sparkline = 'sparkline',
|
||||
}
|
||||
|
||||
@@ -838,7 +839,38 @@ export enum TableCellHeight {
|
||||
* Table cell options. Each cell has a display mode
|
||||
* and other potential options for that display.
|
||||
*/
|
||||
export type TableCellOptions = (TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions);
|
||||
export type TableCellOptions = (TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions);
|
||||
|
||||
/**
|
||||
* Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
|
||||
* Generally defines alignment, filtering capabilties, display options, etc.
|
||||
*/
|
||||
export interface TableFieldOptions {
|
||||
align: FieldTextAlignment;
|
||||
cellOptions: TableCellOptions;
|
||||
/**
|
||||
* This field is deprecated in favor of using cellOptions
|
||||
*/
|
||||
displayMode?: TableCellDisplayMode;
|
||||
filterable?: boolean;
|
||||
hidden?: boolean; // ?? default is missing or false ??
|
||||
/**
|
||||
* Hides any header for a column, useful for columns that show some static content or buttons.
|
||||
*/
|
||||
hideHeader?: boolean;
|
||||
inspect: boolean;
|
||||
minWidth?: number;
|
||||
width?: number;
|
||||
/**
|
||||
* Enables text wrapping for column headers
|
||||
*/
|
||||
wrapHeaderText?: boolean;
|
||||
}
|
||||
|
||||
export const defaultTableFieldOptions: Partial<TableFieldOptions> = {
|
||||
align: 'auto',
|
||||
inspect: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Use UTC/GMT timezone
|
||||
@@ -944,37 +976,12 @@ export enum ComparisonOperation {
|
||||
NEQ = 'neq',
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
|
||||
* Generally defines alignment, filtering capabilties, display options, etc.
|
||||
*/
|
||||
export interface TableFieldOptions {
|
||||
align: FieldTextAlignment;
|
||||
cellOptions: TableCellOptions;
|
||||
/**
|
||||
* This field is deprecated in favor of using cellOptions
|
||||
*/
|
||||
displayMode?: TableCellDisplayMode;
|
||||
filterable?: boolean;
|
||||
hidden?: boolean; // ?? default is missing or false ??
|
||||
/**
|
||||
* Hides any header for a column, useful for columns that show some static content or buttons.
|
||||
*/
|
||||
hideHeader?: boolean;
|
||||
inspect: boolean;
|
||||
minWidth?: number;
|
||||
width?: number;
|
||||
/**
|
||||
* Enables text wrapping for column headers
|
||||
*/
|
||||
wrapHeaderText?: boolean;
|
||||
export interface TablePillCellOptions {
|
||||
color?: string;
|
||||
colorMode?: ('auto' | 'fixed' | 'mapped');
|
||||
type: TableCellDisplayMode.Pill;
|
||||
}
|
||||
|
||||
export const defaultTableFieldOptions: Partial<TableFieldOptions> = {
|
||||
align: 'auto',
|
||||
inspect: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* A specific timezone from https://en.wikipedia.org/wiki/Tz_database
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ package common
|
||||
// in the table such as colored text, JSON, gauge, etc.
|
||||
// The color-background-solid, gradient-gauge, and lcd-gauge
|
||||
// modes are deprecated in favor of new cell subOptions
|
||||
TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-background-solid" | "gradient-gauge" | "lcd-gauge" | "json-view" | "basic" | "image" | "gauge" | "sparkline" | "data-links" | "custom" | "actions" @cuetsy(kind="enum",memberNames="Auto|ColorText|ColorBackground|ColorBackgroundSolid|GradientGauge|LcdGauge|JSONView|BasicGauge|Image|Gauge|Sparkline|DataLinks|Custom|Actions")
|
||||
TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-background-solid" | "gradient-gauge" | "lcd-gauge" | "json-view" | "basic" | "image" | "gauge" | "sparkline" | "data-links" | "custom" | "actions" | "pill" @cuetsy(kind="enum",memberNames="Auto|ColorText|ColorBackground|ColorBackgroundSolid|GradientGauge|LcdGauge|JSONView|BasicGauge|Image|Gauge|Sparkline|DataLinks|Custom|Actions|Pill")
|
||||
|
||||
// Display mode to the "Colored Background" display
|
||||
// mode for table cells. Either displays a solid color (basic mode)
|
||||
@@ -89,7 +89,7 @@ TableCellHeight: "sm" | "md" | "lg" | "auto" @cuetsy(kind="enum")
|
||||
|
||||
// Table cell options. Each cell has a display mode
|
||||
// and other potential options for that display.
|
||||
TableCellOptions: TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions @cuetsy(kind="type")
|
||||
TableCellOptions: TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions @cuetsy(kind="type")
|
||||
|
||||
// Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
|
||||
// Generally defines alignment, filtering capabilties, display options, etc.
|
||||
@@ -109,3 +109,9 @@ TableFieldOptions: {
|
||||
wrapHeaderText?: bool
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
TablePillCellOptions: {
|
||||
type: TableCellDisplayMode & "pill"
|
||||
color?: string
|
||||
colorMode?: "auto" | "fixed" | "mapped"
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.2.0",
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@types/chance": "1.1.6",
|
||||
"@types/chance": "^1.1.7",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/hoist-non-react-statics": "3.3.6",
|
||||
@@ -176,7 +176,7 @@
|
||||
"@types/slate-react": "0.22.9",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/uuid": "10.0.0",
|
||||
"chance": "1.1.12",
|
||||
"chance": "^1.1.13",
|
||||
"common-tags": "1.8.2",
|
||||
"core-js": "3.40.0",
|
||||
"css-loader": "7.1.2",
|
||||
|
||||
@@ -74,8 +74,6 @@ export const DataLinksActionsTooltip = ({ links, actions, value, coords, onToolt
|
||||
|
||||
const dismiss = useDismiss(context);
|
||||
|
||||
const hasMultipleLinksOrActions = links.length > 1 || Boolean(actions?.length);
|
||||
|
||||
const { getFloatingProps, getReferenceProps } = useInteractions([dismiss]);
|
||||
|
||||
if (links.length === 0 && !Boolean(actions?.length)) {
|
||||
@@ -84,23 +82,22 @@ export const DataLinksActionsTooltip = ({ links, actions, value, coords, onToolt
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* TODO: we can remove `value` from this component when tableNextGen is fully rolled out */}
|
||||
{value}
|
||||
{hasMultipleLinksOrActions && (
|
||||
<Portal>
|
||||
<div
|
||||
ref={refCallback}
|
||||
{...getReferenceProps()}
|
||||
{...getFloatingProps()}
|
||||
style={floatingStyles}
|
||||
className={styles.tooltipWrapper}
|
||||
data-testid={selectors.components.DataLinksActionsTooltip.tooltipWrapper}
|
||||
>
|
||||
<VizTooltipWrapper>
|
||||
<VizTooltipFooter dataLinks={links} actions={actions} />
|
||||
</VizTooltipWrapper>
|
||||
</div>
|
||||
</Portal>
|
||||
)}
|
||||
<Portal>
|
||||
<div
|
||||
ref={refCallback}
|
||||
{...getReferenceProps()}
|
||||
{...getFloatingProps()}
|
||||
style={floatingStyles}
|
||||
className={styles.tooltipWrapper}
|
||||
data-testid={selectors.components.DataLinksActionsTooltip.tooltipWrapper}
|
||||
>
|
||||
<VizTooltipWrapper>
|
||||
<VizTooltipFooter dataLinks={links} actions={actions} />
|
||||
</VizTooltipWrapper>
|
||||
</div>
|
||||
</Portal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
@@ -6,12 +7,16 @@ import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { ActionButton } from '../../../Actions/ActionButton';
|
||||
import { ActionCellProps } from '../types';
|
||||
|
||||
export const ActionsCell = ({ actions }: ActionCellProps) => {
|
||||
export const ActionsCell = ({ field, rowIdx, getActions }: ActionCellProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const actions = useMemo(() => getActions(field, rowIdx), [getActions, field, rowIdx]);
|
||||
|
||||
return (
|
||||
<div className={styles.buttonsGap}>
|
||||
{actions && actions.map((action, i) => <ActionButton key={i} action={action} variant="secondary" />)}
|
||||
{actions.map((action, i) => (
|
||||
<ActionButton key={i} action={action} variant="secondary" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { Field, FieldType, LinkModel } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { TableCellDisplayMode } from '../../types';
|
||||
|
||||
import AutoCell from './AutoCell';
|
||||
|
||||
describe('AutoCell', () => {
|
||||
describe('Displays data Links', () => {
|
||||
const getFieldWithLinks = (links: LinkModel[]): Field => {
|
||||
return {
|
||||
name: 'Category',
|
||||
type: FieldType.string,
|
||||
values: ['A', 'B', 'A', 'B', 'A'],
|
||||
config: {
|
||||
custom: {
|
||||
cellOptions: {
|
||||
type: TableCellDisplayMode.Auto,
|
||||
wrapText: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
display: (value: unknown) => ({
|
||||
text: String(value),
|
||||
numeric: 0,
|
||||
color: undefined,
|
||||
prefix: undefined,
|
||||
suffix: undefined,
|
||||
}),
|
||||
state: {},
|
||||
getLinks: () => links,
|
||||
};
|
||||
};
|
||||
|
||||
it('shows multiple datalinks in the tooltip', async () => {
|
||||
const linksForField = [
|
||||
{ href: 'http://asdasd.com', title: 'Test Title' } as LinkModel,
|
||||
{ href: 'http://asdasd2.com', title: 'Test Title2' } as LinkModel,
|
||||
];
|
||||
|
||||
jest.mock('../utils', () => ({
|
||||
getCellLinks: () => linksForField,
|
||||
}));
|
||||
|
||||
const field = getFieldWithLinks(linksForField);
|
||||
|
||||
render(
|
||||
<AutoCell
|
||||
value="test"
|
||||
field={field}
|
||||
justifyContent="normal"
|
||||
rowIdx={0}
|
||||
cellOptions={{ type: TableCellDisplayMode.Auto }}
|
||||
/>
|
||||
);
|
||||
|
||||
const cell = screen.getByTestId(selectors.components.TablePanel.autoCell);
|
||||
await userEvent.click(cell);
|
||||
|
||||
const tooltip = screen.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper);
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Title2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show tooltip for multiple links if one is invalid', async () => {
|
||||
const linksForField = [
|
||||
{ href: 'http://asdasd.com', title: 'Test Title' } as LinkModel,
|
||||
{ title: 'Test Title2' } as LinkModel,
|
||||
];
|
||||
|
||||
jest.mock('../utils', () => ({
|
||||
getCellLinks: () => linksForField,
|
||||
}));
|
||||
|
||||
const field = getFieldWithLinks(linksForField);
|
||||
|
||||
render(
|
||||
<AutoCell
|
||||
value="test"
|
||||
field={field}
|
||||
justifyContent="normal"
|
||||
rowIdx={0}
|
||||
cellOptions={{ type: TableCellDisplayMode.Auto }}
|
||||
/>
|
||||
);
|
||||
|
||||
const cell = screen.getByTestId(selectors.components.TablePanel.autoCell);
|
||||
await userEvent.click(cell);
|
||||
|
||||
expect(screen.queryByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,49 +1,24 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, formattedValueToString } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { DataLinksActionsTooltip, renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { TableCellOptions, TableCellDisplayMode } from '../../types';
|
||||
import { DataLinksActionsTooltipCoords, getDataLinksActionsTooltipUtils, tooltipOnClickHandler } from '../../utils';
|
||||
import { useSingleLink } from '../hooks';
|
||||
import { AutoCellProps } from '../types';
|
||||
import { getCellLinks } from '../utils';
|
||||
|
||||
export default function AutoCell({ value, field, justifyContent, rowIdx, cellOptions, actions }: AutoCellProps) {
|
||||
export default function AutoCell({ value, field, justifyContent, rowIdx, cellOptions }: AutoCellProps) {
|
||||
const styles = useStyles2(getStyles, justifyContent);
|
||||
|
||||
const displayValue = field.display!(value);
|
||||
const formattedValue = formattedValueToString(displayValue);
|
||||
const links = getCellLinks(field, rowIdx) || [];
|
||||
|
||||
const [tooltipCoords, setTooltipCoords] = useState<DataLinksActionsTooltipCoords>();
|
||||
const { shouldShowLink, hasMultipleLinksOrActions } = getDataLinksActionsTooltipUtils(links, actions);
|
||||
const shouldShowTooltip = hasMultipleLinksOrActions && tooltipCoords !== undefined;
|
||||
const link = useSingleLink(field, rowIdx);
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className={styles.cell}
|
||||
onClick={tooltipOnClickHandler(setTooltipCoords)}
|
||||
style={{ cursor: hasMultipleLinksOrActions ? 'context-menu' : 'auto' }}
|
||||
data-testid={selectors.components.TablePanel.autoCell}
|
||||
>
|
||||
{shouldShowLink ? (
|
||||
renderSingleLink(links[0], formattedValue, getLinkStyle(styles, cellOptions))
|
||||
) : shouldShowTooltip ? (
|
||||
<DataLinksActionsTooltip
|
||||
links={links}
|
||||
actions={actions}
|
||||
value={formattedValue}
|
||||
coords={tooltipCoords}
|
||||
onTooltipClose={() => setTooltipCoords(undefined)}
|
||||
/>
|
||||
) : (
|
||||
formattedValue
|
||||
)}
|
||||
<div className={styles.cell}>
|
||||
{link == null ? formattedValue : renderSingleLink(link, formattedValue, getLinkStyle(styles, cellOptions))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { ThresholdsConfig, ThresholdsMode, VizOrientation, getFieldConfigWithMinMax } from '@grafana/data';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode, TableCellDisplayMode } from '@grafana/schema';
|
||||
|
||||
import { BarGauge } from '../../../BarGauge/BarGauge';
|
||||
import { DataLinksActionsTooltip, renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { tooltipOnClickHandler, DataLinksActionsTooltipCoords, getDataLinksActionsTooltipUtils } from '../../utils';
|
||||
import { renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { useSingleLink } from '../hooks';
|
||||
import { BarGaugeCellProps } from '../types';
|
||||
import { extractPixelValue, getCellOptions, getAlignmentFactor, getCellLinks } from '../utils';
|
||||
import { extractPixelValue, getCellOptions, getAlignmentFactor } from '../utils';
|
||||
|
||||
const defaultScale: ThresholdsConfig = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
@@ -23,7 +21,7 @@ const defaultScale: ThresholdsConfig = {
|
||||
],
|
||||
};
|
||||
|
||||
export const BarGaugeCell = ({ value, field, theme, height, width, rowIdx, actions }: BarGaugeCellProps) => {
|
||||
export const BarGaugeCell = ({ value, field, theme, height, width, rowIdx }: BarGaugeCellProps) => {
|
||||
const displayValue = field.display!(value);
|
||||
const cellOptions = getCellOptions(field);
|
||||
const heightOffset = extractPixelValue(theme.spacing(1));
|
||||
@@ -48,51 +46,26 @@ export const BarGaugeCell = ({ value, field, theme, height, width, rowIdx, actio
|
||||
}
|
||||
|
||||
const alignmentFactors = getAlignmentFactor(field, displayValue, rowIdx!);
|
||||
const links = getCellLinks(field, rowIdx) || [];
|
||||
|
||||
const [tooltipCoords, setTooltipCoords] = useState<DataLinksActionsTooltipCoords>();
|
||||
const { shouldShowLink, hasMultipleLinksOrActions } = getDataLinksActionsTooltipUtils(links, actions);
|
||||
const shouldShowTooltip = hasMultipleLinksOrActions && tooltipCoords !== undefined;
|
||||
|
||||
const renderComponent = () => {
|
||||
return (
|
||||
<BarGauge
|
||||
width={width}
|
||||
height={height - heightOffset}
|
||||
field={config}
|
||||
display={field.display}
|
||||
text={{ valueSize: 14 }}
|
||||
value={displayValue}
|
||||
orientation={VizOrientation.Horizontal}
|
||||
theme={theme}
|
||||
alignmentFactors={alignmentFactors}
|
||||
itemSpacing={1}
|
||||
lcdCellWidth={8}
|
||||
displayMode={barGaugeMode}
|
||||
valueDisplayMode={valueDisplayMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
style={{ cursor: hasMultipleLinksOrActions ? 'context-menu' : 'auto' }}
|
||||
onClick={tooltipOnClickHandler(setTooltipCoords)}
|
||||
>
|
||||
{shouldShowLink ? (
|
||||
renderSingleLink(links[0], renderComponent())
|
||||
) : shouldShowTooltip ? (
|
||||
<DataLinksActionsTooltip
|
||||
links={links}
|
||||
actions={actions}
|
||||
value={renderComponent()}
|
||||
coords={tooltipCoords}
|
||||
onTooltipClose={() => setTooltipCoords(undefined)}
|
||||
/>
|
||||
) : (
|
||||
renderComponent()
|
||||
)}
|
||||
</div>
|
||||
const barGaugeComponent = (
|
||||
<BarGauge
|
||||
width={width}
|
||||
height={height - heightOffset}
|
||||
field={config}
|
||||
display={field.display}
|
||||
text={{ valueSize: 14 }}
|
||||
value={displayValue}
|
||||
orientation={VizOrientation.Horizontal}
|
||||
theme={theme}
|
||||
alignmentFactors={alignmentFactors}
|
||||
itemSpacing={1}
|
||||
lcdCellWidth={8}
|
||||
displayMode={barGaugeMode}
|
||||
valueDisplayMode={valueDisplayMode}
|
||||
/>
|
||||
);
|
||||
|
||||
const link = useSingleLink(field, rowIdx);
|
||||
|
||||
return link == null ? barGaugeComponent : renderSingleLink(link, barGaugeComponent);
|
||||
};
|
||||
|
||||
@@ -1,55 +1,28 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { DataLinksActionsTooltip, renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { TableCellDisplayMode } from '../../types';
|
||||
import { DataLinksActionsTooltipCoords, getDataLinksActionsTooltipUtils, tooltipOnClickHandler } from '../../utils';
|
||||
import { useSingleLink } from '../hooks';
|
||||
import { ImageCellProps } from '../types';
|
||||
import { getCellLinks } from '../utils';
|
||||
|
||||
const DATALINKS_HEIGHT_OFFSET = 10;
|
||||
|
||||
export const ImageCell = ({ cellOptions, field, height, justifyContent, value, rowIdx, actions }: ImageCellProps) => {
|
||||
export const ImageCell = ({ cellOptions, field, height, justifyContent, value, rowIdx }: ImageCellProps) => {
|
||||
const calculatedHeight = height - DATALINKS_HEIGHT_OFFSET;
|
||||
const styles = useStyles2(getStyles, calculatedHeight, justifyContent);
|
||||
const links = getCellLinks(field, rowIdx) || [];
|
||||
|
||||
const [tooltipCoords, setTooltipCoords] = useState<DataLinksActionsTooltipCoords>();
|
||||
const { shouldShowLink, hasMultipleLinksOrActions } = getDataLinksActionsTooltipUtils(links, actions);
|
||||
const shouldShowTooltip = hasMultipleLinksOrActions && tooltipCoords !== undefined;
|
||||
|
||||
const { text } = field.display!(value);
|
||||
const { alt, title } =
|
||||
cellOptions.type === TableCellDisplayMode.Image ? cellOptions : { alt: undefined, title: undefined };
|
||||
|
||||
const img = <img alt={alt} src={text} className={styles.image} title={title} />;
|
||||
const link = useSingleLink(field, rowIdx);
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className={styles.imageContainer}
|
||||
style={{ cursor: hasMultipleLinksOrActions ? 'context-menu' : 'auto' }}
|
||||
onClick={tooltipOnClickHandler(setTooltipCoords)}
|
||||
>
|
||||
{shouldShowLink ? (
|
||||
renderSingleLink(links[0], img)
|
||||
) : shouldShowTooltip ? (
|
||||
<DataLinksActionsTooltip
|
||||
links={links}
|
||||
actions={actions}
|
||||
value={img}
|
||||
coords={tooltipCoords}
|
||||
onTooltipClose={() => setTooltipCoords(undefined)}
|
||||
/>
|
||||
) : (
|
||||
img
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return <div className={styles.imageContainer}>{link == null ? img : renderSingleLink(link, img)}</div>;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, height: number, justifyContent: Property.JustifyContent) => ({
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { DataLinksActionsTooltip, renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { tooltipOnClickHandler, DataLinksActionsTooltipCoords, getDataLinksActionsTooltipUtils } from '../../utils';
|
||||
import { renderSingleLink } from '../../DataLinksActionsTooltip';
|
||||
import { useSingleLink } from '../hooks';
|
||||
import { JSONCellProps } from '../types';
|
||||
import { getCellLinks } from '../utils';
|
||||
|
||||
export const JSONCell = ({ value, justifyContent, field, rowIdx, actions }: JSONCellProps) => {
|
||||
export const JSONCell = ({ value, justifyContent, field, rowIdx }: JSONCellProps) => {
|
||||
const styles = useStyles2(getStyles, justifyContent);
|
||||
|
||||
let displayValue = value;
|
||||
@@ -33,34 +31,9 @@ export const JSONCell = ({ value, justifyContent, field, rowIdx, actions }: JSON
|
||||
}
|
||||
}
|
||||
|
||||
const links = getCellLinks(field, rowIdx) || [];
|
||||
const link = useSingleLink(field, rowIdx);
|
||||
|
||||
const [tooltipCoords, setTooltipCoords] = useState<DataLinksActionsTooltipCoords>();
|
||||
const { shouldShowLink, hasMultipleLinksOrActions } = getDataLinksActionsTooltipUtils(links, actions);
|
||||
const shouldShowTooltip = hasMultipleLinksOrActions && tooltipCoords !== undefined;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className={styles.jsonText}
|
||||
onClick={tooltipOnClickHandler(setTooltipCoords)}
|
||||
style={{ cursor: hasMultipleLinksOrActions ? 'context-menu' : 'auto' }}
|
||||
>
|
||||
{shouldShowLink ? (
|
||||
renderSingleLink(links[0], displayValue)
|
||||
) : shouldShowTooltip ? (
|
||||
<DataLinksActionsTooltip
|
||||
links={links}
|
||||
actions={actions}
|
||||
value={displayValue}
|
||||
coords={tooltipCoords}
|
||||
onTooltipClose={() => setTooltipCoords(undefined)}
|
||||
/>
|
||||
) : (
|
||||
displayValue
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return <div className={styles.jsonText}>{link == null ? displayValue : renderSingleLink(link, displayValue)}</div>;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, justifyContent: Property.JustifyContent) => ({
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { DataFrame, Field, FieldType, GrafanaTheme2, MappingType, createTheme } from '@grafana/data';
|
||||
import { TableCellDisplayMode, TablePillCellOptions } from '@grafana/schema';
|
||||
|
||||
import { mockThemeContext } from '../../../../themes/ThemeContext';
|
||||
|
||||
import { PillCell, inferPills } from './PillCell';
|
||||
|
||||
describe('PillCell', () => {
|
||||
let restoreThemeContext: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
restoreThemeContext = mockThemeContext(createTheme());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreThemeContext();
|
||||
});
|
||||
|
||||
const mockCellOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'auto',
|
||||
};
|
||||
|
||||
const mockField: Field = {
|
||||
name: 'test',
|
||||
type: FieldType.string,
|
||||
values: [],
|
||||
config: {},
|
||||
};
|
||||
|
||||
const mockFrame: DataFrame = {
|
||||
name: 'test',
|
||||
fields: [mockField],
|
||||
length: 1,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
value: 'test-value',
|
||||
field: mockField,
|
||||
justifyContent: 'flex-start' as const,
|
||||
cellOptions: mockCellOptions,
|
||||
rowIdx: 0,
|
||||
frame: mockFrame,
|
||||
height: 30,
|
||||
width: 100,
|
||||
theme: {} as GrafanaTheme2,
|
||||
cellInspect: false,
|
||||
showFilters: false,
|
||||
};
|
||||
|
||||
describe('pill parsing', () => {
|
||||
it('should render pills for single values', () => {
|
||||
render(<PillCell {...defaultProps} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render pills for CSV values', () => {
|
||||
render(<PillCell {...defaultProps} value="value1,value2,value3" />);
|
||||
expect(screen.getByText('value1')).toBeInTheDocument();
|
||||
expect(screen.getByText('value2')).toBeInTheDocument();
|
||||
expect(screen.getByText('value3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render pills for JSON array values', () => {
|
||||
render(<PillCell {...defaultProps} value='["item1","item2","item3"]' />);
|
||||
expect(screen.getByText('item1')).toBeInTheDocument();
|
||||
expect(screen.getByText('item2')).toBeInTheDocument();
|
||||
expect(screen.getByText('item3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show dash for empty values', () => {
|
||||
render(<PillCell {...defaultProps} value="" />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show dash for null values', () => {
|
||||
render(<PillCell {...defaultProps} value={null as unknown as string} />);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('color mapping', () => {
|
||||
// These tests primarily ensure the color logic executes without throwing.
|
||||
// For true color verification, visual regression tests would be needed.
|
||||
|
||||
it('should use mapped colors when colorMode is mapped', () => {
|
||||
const mappedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'mapped',
|
||||
};
|
||||
|
||||
render(<PillCell {...defaultProps} value="success,error,warning,unknown" cellOptions={mappedOptions} />);
|
||||
|
||||
const successPill = screen.getByText('success');
|
||||
const errorPill = screen.getByText('error');
|
||||
const warningPill = screen.getByText('warning');
|
||||
const unknownPill = screen.getByText('unknown');
|
||||
|
||||
expect(successPill).toBeInTheDocument();
|
||||
expect(errorPill).toBeInTheDocument();
|
||||
expect(warningPill).toBeInTheDocument();
|
||||
expect(unknownPill).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use field-level value mappings when available', () => {
|
||||
const mappedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'mapped',
|
||||
};
|
||||
|
||||
// Mock field with value mappings
|
||||
const fieldWithMappings: Field = {
|
||||
...mockField,
|
||||
config: {
|
||||
...mockField.config,
|
||||
mappings: [
|
||||
{
|
||||
type: MappingType.ValueToText,
|
||||
options: {
|
||||
success: { color: '#00FF00' },
|
||||
error: { color: '#FF0000' },
|
||||
warning: { color: '#FFFF00' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
display: (value: unknown) => ({
|
||||
text: String(value),
|
||||
color:
|
||||
String(value) === 'success'
|
||||
? '#00FF00'
|
||||
: String(value) === 'error'
|
||||
? '#FF0000'
|
||||
: String(value) === 'warning'
|
||||
? '#FFFF00'
|
||||
: '#FF780A',
|
||||
numeric: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
render(
|
||||
<PillCell
|
||||
{...defaultProps}
|
||||
value="success,error,warning,unknown"
|
||||
cellOptions={mappedOptions}
|
||||
field={fieldWithMappings}
|
||||
/>
|
||||
);
|
||||
|
||||
const successPill = screen.getByText('success');
|
||||
const errorPill = screen.getByText('error');
|
||||
const warningPill = screen.getByText('warning');
|
||||
const unknownPill = screen.getByText('unknown');
|
||||
|
||||
expect(successPill).toBeInTheDocument();
|
||||
expect(errorPill).toBeInTheDocument();
|
||||
expect(warningPill).toBeInTheDocument();
|
||||
expect(unknownPill).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use fixed color when colorMode is fixed', () => {
|
||||
const fixedOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'fixed',
|
||||
color: '#FF00FF',
|
||||
};
|
||||
|
||||
render(<PillCell {...defaultProps} cellOptions={fixedOptions} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use auto color when colorMode is auto', () => {
|
||||
const autoOptions: TablePillCellOptions = {
|
||||
type: TableCellDisplayMode.Pill,
|
||||
colorMode: 'auto',
|
||||
};
|
||||
|
||||
render(<PillCell {...defaultProps} cellOptions={autoOptions} />);
|
||||
expect(screen.getByText('test-value')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inferPills', () => {
|
||||
// These tests verify the pill parsing logic handles various input formats correctly.
|
||||
// They ensure the function can extract pill values from different data structures.
|
||||
|
||||
it('should return empty array for null/undefined values', () => {
|
||||
expect(inferPills(null)).toEqual([]);
|
||||
expect(inferPills(undefined)).toEqual([]);
|
||||
expect(inferPills('')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should parse single values', () => {
|
||||
expect(inferPills('test')).toEqual(['test']);
|
||||
expect(inferPills('"quoted"')).toEqual(['quoted']);
|
||||
expect(inferPills("'quoted'")).toEqual(['quoted']);
|
||||
});
|
||||
|
||||
it('should parse CSV strings', () => {
|
||||
expect(inferPills('value1,value2,value3')).toEqual(['value1', 'value2', 'value3']);
|
||||
expect(inferPills(' value1 , value2 , value3 ')).toEqual(['value1', 'value2', 'value3']);
|
||||
expect(inferPills('value1, ,value3')).toEqual(['value1', 'value3']);
|
||||
});
|
||||
|
||||
it('should parse JSON arrays', () => {
|
||||
expect(inferPills('["item1","item2","item3"]')).toEqual(['item1', 'item2', 'item3']);
|
||||
expect(inferPills('["item1", "item2", "item3"]')).toEqual(['item1', 'item2', 'item3']);
|
||||
expect(inferPills('["item1", null, "item3"]')).toEqual(['item1', 'item3']);
|
||||
});
|
||||
|
||||
it('should handle mixed content', () => {
|
||||
// When JSON parsing fails, it falls back to CSV parsing
|
||||
expect(inferPills('["item1", "item2"],extra')).toEqual(['["item1"', '"item2"]', 'extra']);
|
||||
expect(inferPills('not-json,value')).toEqual(['not-json', 'value']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,185 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2, isDataFrame, classicColors, colorManipulator, Field } from '@grafana/data';
|
||||
import { TablePillCellOptions } from '@grafana/schema';
|
||||
|
||||
import { useStyles2 } from '../../../../themes/ThemeContext';
|
||||
import { TableCellRendererProps } from '../types';
|
||||
|
||||
const DEFAULT_PILL_BG_COLOR = '#FF780A';
|
||||
|
||||
interface Pill {
|
||||
value: string;
|
||||
key: string;
|
||||
bgColor: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
function createPills(pillValues: string[], cellOptions: TableCellRendererProps['cellOptions'], field: Field): Pill[] {
|
||||
return pillValues.map((pill, index) => {
|
||||
const bgColor = getPillColor(pill, cellOptions, field);
|
||||
const textColor = colorManipulator.getContrastRatio('#FFFFFF', bgColor) >= 4.5 ? '#FFFFFF' : '#000000';
|
||||
return {
|
||||
value: pill,
|
||||
key: `${pill}-${index}`,
|
||||
bgColor,
|
||||
color: textColor,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function PillCell({ value, field, justifyContent, cellOptions }: TableCellRendererProps) {
|
||||
const styles = useStyles2(getStyles, justifyContent);
|
||||
|
||||
const pills: Pill[] = useMemo(() => {
|
||||
const pillValues = inferPills(value);
|
||||
return createPills(pillValues, cellOptions, field);
|
||||
}, [value, cellOptions, field]);
|
||||
|
||||
if (pills.length === 0) {
|
||||
return <div className={styles.cell}>-</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.cell}>
|
||||
<div className={styles.pillsContainer}>
|
||||
{pills.map((pill) => (
|
||||
<span
|
||||
key={pill.key}
|
||||
className={styles.pill}
|
||||
style={{
|
||||
backgroundColor: pill.bgColor,
|
||||
color: pill.color,
|
||||
}}
|
||||
>
|
||||
{pill.value}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function inferPills(value: unknown): string[] {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Handle DataFrame - not supported for pills
|
||||
if (isDataFrame(value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Handle different value types
|
||||
const stringValue = String(value);
|
||||
|
||||
// Try to parse as JSON first
|
||||
try {
|
||||
const parsed = JSON.parse(stringValue);
|
||||
if (Array.isArray(parsed)) {
|
||||
// JSON array of strings
|
||||
return parsed
|
||||
.filter((item) => item != null && item !== '')
|
||||
.map(String)
|
||||
.map((text) => text.trim())
|
||||
.filter((item) => item !== '');
|
||||
}
|
||||
} catch {
|
||||
// Not valid JSON, continue with other parsing
|
||||
}
|
||||
|
||||
// Handle CSV string
|
||||
if (stringValue.includes(',')) {
|
||||
return stringValue
|
||||
.split(',')
|
||||
.map((text) => text.trim())
|
||||
.filter((item) => item !== '');
|
||||
}
|
||||
|
||||
// Single value - strip quotes
|
||||
return [stringValue.replace(/["'`]/g, '').trim()];
|
||||
}
|
||||
|
||||
function isPillCellOptions(cellOptions: TableCellRendererProps['cellOptions']): cellOptions is TablePillCellOptions {
|
||||
return cellOptions?.type === 'pill';
|
||||
}
|
||||
|
||||
function getPillColor(pill: string, cellOptions: TableCellRendererProps['cellOptions'], field: Field): string {
|
||||
if (!isPillCellOptions(cellOptions)) {
|
||||
return getDeterministicColor(pill);
|
||||
}
|
||||
|
||||
const colorMode = cellOptions.colorMode || 'auto';
|
||||
|
||||
// Fixed color mode (highest priority)
|
||||
if (colorMode === 'fixed' && cellOptions.color) {
|
||||
return cellOptions.color;
|
||||
}
|
||||
|
||||
// Mapped color mode - use field's value mappings
|
||||
if (colorMode === 'mapped') {
|
||||
// Check if field has value mappings
|
||||
if (field.config.mappings && field.config.mappings.length > 0) {
|
||||
// Use the field's display processor to get the mapped value
|
||||
const displayValue = field.display!(pill);
|
||||
if (displayValue.color) {
|
||||
return displayValue.color;
|
||||
}
|
||||
}
|
||||
// Fallback to default color for unmapped values
|
||||
return cellOptions.color || DEFAULT_PILL_BG_COLOR;
|
||||
}
|
||||
|
||||
// Auto mode - deterministic color assignment based on string hash
|
||||
if (colorMode === 'auto') {
|
||||
return getDeterministicColor(pill);
|
||||
}
|
||||
|
||||
// Default color for unknown values or fallback
|
||||
return DEFAULT_PILL_BG_COLOR;
|
||||
}
|
||||
|
||||
function getDeterministicColor(text: string): string {
|
||||
// Create a simple hash of the string to get consistent colors
|
||||
let hash = 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32-bit integer
|
||||
}
|
||||
|
||||
// Use absolute value and modulo to get a consistent index
|
||||
const colorValues = Object.values(classicColors);
|
||||
const index = Math.abs(hash) % colorValues.length;
|
||||
|
||||
return colorValues[index];
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, justifyContent: Property.JustifyContent | undefined) => ({
|
||||
cell: css({
|
||||
display: 'flex',
|
||||
justifyContent: justifyContent || 'flex-start',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
padding: theme.spacing(0.5),
|
||||
}),
|
||||
pillsContainer: css({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: theme.spacing(0.5),
|
||||
maxWidth: '100%',
|
||||
}),
|
||||
pill: css({
|
||||
display: 'inline-block',
|
||||
padding: theme.spacing(0.25, 0.75),
|
||||
borderRadius: theme.shape.radius.default,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
lineHeight: theme.typography.bodySmall.lineHeight,
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
whiteSpace: 'nowrap',
|
||||
textAlign: 'center',
|
||||
minWidth: 'fit-content',
|
||||
}),
|
||||
});
|
||||
@@ -24,7 +24,10 @@ export function TableCellActions(props: TableCellActionsProps) {
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
// stopping propagation to prevent clicks within the actions menu from triggering the cell click events
|
||||
// for things like the data links tooltip.
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div className={className} onClick={(ev) => ev.stopPropagation()}>
|
||||
{cellInspect && (
|
||||
<IconButton
|
||||
name="eye"
|
||||
|
||||
@@ -12,6 +12,7 @@ import { DataLinksCell } from './DataLinksCell';
|
||||
import { GeoCell } from './GeoCell';
|
||||
import { ImageCell } from './ImageCell';
|
||||
import { JSONCell } from './JSONCell';
|
||||
import { PillCell } from './PillCell';
|
||||
import { SparklineCell } from './SparklineCell';
|
||||
|
||||
export type TableCellRenderer = (props: TableCellRendererProps) => ReactNode;
|
||||
@@ -24,7 +25,6 @@ const GAUGE_RENDERER: TableCellRenderer = (props) => (
|
||||
height={props.height}
|
||||
width={props.width}
|
||||
rowIdx={props.rowIdx}
|
||||
actions={props.actions}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,6 @@ const AUTO_RENDERER: TableCellRenderer = (props) => (
|
||||
justifyContent={props.justifyContent}
|
||||
rowIdx={props.rowIdx}
|
||||
cellOptions={props.cellOptions}
|
||||
actions={props.actions}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -52,13 +51,7 @@ const SPARKLINE_RENDERER: TableCellRenderer = (props) => (
|
||||
);
|
||||
|
||||
const JSON_RENDERER: TableCellRenderer = (props) => (
|
||||
<JSONCell
|
||||
justifyContent={props.justifyContent}
|
||||
value={props.value}
|
||||
field={props.field}
|
||||
rowIdx={props.rowIdx}
|
||||
actions={props.actions}
|
||||
/>
|
||||
<JSONCell justifyContent={props.justifyContent} value={props.value} field={props.field} rowIdx={props.rowIdx} />
|
||||
);
|
||||
|
||||
const GEO_RENDERER: TableCellRenderer = (props) => (
|
||||
@@ -73,13 +66,16 @@ const IMAGE_RENDERER: TableCellRenderer = (props) => (
|
||||
justifyContent={props.justifyContent}
|
||||
value={props.value}
|
||||
rowIdx={props.rowIdx}
|
||||
actions={props.actions}
|
||||
/>
|
||||
);
|
||||
|
||||
const DATA_LINKS_RENDERER: TableCellRenderer = (props) => <DataLinksCell field={props.field} rowIdx={props.rowIdx} />;
|
||||
|
||||
const ACTIONS_RENDERER: TableCellRenderer = (props) => <ActionsCell actions={props.actions} />;
|
||||
const ACTIONS_RENDERER: TableCellRenderer = ({ field, rowIdx, getActions = () => [] }) => (
|
||||
<ActionsCell field={field} rowIdx={rowIdx} getActions={getActions} />
|
||||
);
|
||||
|
||||
const PILL_RENDERER: TableCellRenderer = (props) => <PillCell {...props} />;
|
||||
|
||||
function isCustomCellOptions(options: TableCellOptions): options is TableCustomCellOptions {
|
||||
return options.type === TableCellDisplayMode.Custom;
|
||||
@@ -104,6 +100,7 @@ const CELL_RENDERERS: Record<TableCellOptions['type'], TableCellRenderer> = {
|
||||
[TableCellDisplayMode.ColorText]: AUTO_RENDERER,
|
||||
[TableCellDisplayMode.ColorBackground]: AUTO_RENDERER,
|
||||
[TableCellDisplayMode.Auto]: AUTO_RENDERER,
|
||||
[TableCellDisplayMode.Pill]: PILL_RENDERER,
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { applyFieldOverrides, createTheme, DataFrame, EventBus, FieldType, toDataFrame } from '@grafana/data';
|
||||
import {
|
||||
applyFieldOverrides,
|
||||
createTheme,
|
||||
DataFrame,
|
||||
DataLink,
|
||||
EventBus,
|
||||
FieldType,
|
||||
LinkModel,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { TableCellBackgroundDisplayMode } from '@grafana/schema';
|
||||
|
||||
import { PanelContext, PanelContextProvider } from '../../../components/PanelChrome';
|
||||
@@ -1682,4 +1692,55 @@ describe('TableNG', () => {
|
||||
expect(mockEventBus.publish).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Displays data Links', () => {
|
||||
function toLinkModel(link: DataLink): LinkModel {
|
||||
return {
|
||||
href: link.url,
|
||||
title: link.title,
|
||||
target: link.targetBlank ? '_blank' : '_self',
|
||||
origin: link.origin || 'panel',
|
||||
};
|
||||
}
|
||||
|
||||
it('shows multiple datalinks in the tooltip', async () => {
|
||||
const dataFrame = createBasicDataFrame();
|
||||
const links: DataLink[] = [
|
||||
{ url: 'http://asdasd.com', title: 'Test Title' },
|
||||
{ url: 'http://asdasd2.com', title: 'Test Title2' },
|
||||
];
|
||||
|
||||
dataFrame.fields[0].config.links = links;
|
||||
dataFrame.fields[0].getLinks = () => links.map(toLinkModel);
|
||||
|
||||
render(<TableNG enableVirtualization={false} data={dataFrame} width={800} height={600} />);
|
||||
|
||||
const cell = screen.getByText('A1');
|
||||
await userEvent.click(cell);
|
||||
|
||||
const tooltip = screen.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper);
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Title2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show tooltip for a single link', async () => {
|
||||
const dataFrame = createBasicDataFrame();
|
||||
|
||||
const links: DataLink[] = [{ url: 'http://asdasd.com', title: 'Test Title' }];
|
||||
|
||||
dataFrame.fields[0].config.links = links;
|
||||
dataFrame.fields[0].getLinks = () => links.map(toLinkModel);
|
||||
|
||||
render(<TableNG enableVirtualization={false} data={dataFrame} width={800} height={600} />);
|
||||
|
||||
const cell = screen.getByText('A1');
|
||||
|
||||
// we need to click the parent since the cell itself is a link.
|
||||
await userEvent.click(cell.parentElement!);
|
||||
|
||||
expect(screen.queryByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'react-data-grid/lib/styles.css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
import { Key, ReactNode, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { Key, ReactNode, useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Cell,
|
||||
CellRendererProps,
|
||||
@@ -22,8 +22,10 @@ import { ContextMenu } from '../../ContextMenu/ContextMenu';
|
||||
import { MenuItem } from '../../Menu/MenuItem';
|
||||
import { Pagination } from '../../Pagination/Pagination';
|
||||
import { PanelContext, usePanelContext } from '../../PanelChrome';
|
||||
import { DataLinksActionsTooltip } from '../DataLinksActionsTooltip';
|
||||
import { TableCellInspector, TableCellInspectorMode } from '../TableCellInspector';
|
||||
import { CellColors, TableCellDisplayMode } from '../types';
|
||||
import { DataLinksActionsTooltipState } from '../utils';
|
||||
|
||||
import { HeaderCell } from './Cells/HeaderCell';
|
||||
import { RowExpander } from './Cells/RowExpander';
|
||||
@@ -56,6 +58,8 @@ import {
|
||||
getCellOptions,
|
||||
shouldTextWrap,
|
||||
isCellInspectEnabled,
|
||||
getCellLinks,
|
||||
withDataLinksActionsTooltip,
|
||||
} from './utils';
|
||||
|
||||
type CellRootRenderer = (key: React.Key, props: CellRendererProps<TableRow, TableSummaryRow>) => React.ReactNode;
|
||||
@@ -68,14 +72,13 @@ export function TableNG(props: TableNGProps) {
|
||||
enableSharedCrosshair = false,
|
||||
enableVirtualization,
|
||||
footerOptions,
|
||||
getActions,
|
||||
getActions = () => [],
|
||||
height,
|
||||
initialSortBy,
|
||||
noHeader,
|
||||
onCellFilterAdded,
|
||||
onColumnResize,
|
||||
onSortByChange,
|
||||
replaceVariables,
|
||||
showTypeIcons,
|
||||
structureRev,
|
||||
width,
|
||||
@@ -88,6 +91,11 @@ export function TableNG(props: TableNGProps) {
|
||||
});
|
||||
const panelContext = usePanelContext();
|
||||
|
||||
const getCellActions = useCallback(
|
||||
(field: Field, rowIdx: number) => getActions(data, field, rowIdx),
|
||||
[getActions, data]
|
||||
);
|
||||
|
||||
const hasHeader = !noHeader;
|
||||
const hasFooter = Boolean(footerOptions?.show && footerOptions.reducer?.length);
|
||||
const isCountRowsSet = Boolean(
|
||||
@@ -256,13 +264,15 @@ export function TableNG(props: TableNGProps) {
|
||||
interface Schema {
|
||||
columns: TableColumn[];
|
||||
cellRootRenderers: Record<string, CellRootRenderer>;
|
||||
colsWithTooltip: Record<string, boolean>;
|
||||
}
|
||||
|
||||
const { columns, cellRootRenderers } = useMemo(() => {
|
||||
const { columns, cellRootRenderers, colsWithTooltip } = useMemo(() => {
|
||||
const fromFields = (f: Field[], widths: number[]) => {
|
||||
const result: Schema = {
|
||||
columns: [],
|
||||
cellRootRenderers: {},
|
||||
colsWithTooltip: {},
|
||||
};
|
||||
|
||||
let lastRowIdx = -1;
|
||||
@@ -280,7 +290,6 @@ export function TableNG(props: TableNGProps) {
|
||||
const showFilters = Boolean(field.config.filterable && onCellFilterAdded != null);
|
||||
const showActions = cellInspect || showFilters;
|
||||
const width = widths[i];
|
||||
const frame = data;
|
||||
|
||||
// helps us avoid string cx and emotion per-cell
|
||||
const cellActionClassName = showActions
|
||||
@@ -294,6 +303,9 @@ export function TableNG(props: TableNGProps) {
|
||||
const cellType = cellOptions.type;
|
||||
const shouldOverflow = shouldTextOverflow(field);
|
||||
const shouldWrap = shouldTextWrap(field);
|
||||
const withTooltip = withDataLinksActionsTooltip(field, cellType);
|
||||
|
||||
result.colsWithTooltip[displayName] = withTooltip;
|
||||
|
||||
// this fires first
|
||||
const renderCellRoot = (key: Key, props: CellRendererProps<TableRow, TableSummaryRow>): ReactNode => {
|
||||
@@ -317,7 +329,7 @@ export function TableNG(props: TableNGProps) {
|
||||
colors = {};
|
||||
}
|
||||
|
||||
const cellStyle = getCellStyles(theme, field, _rowHeight, shouldWrap, shouldOverflow, colors);
|
||||
const cellStyle = getCellStyles(theme, field, _rowHeight, shouldWrap, shouldOverflow, withTooltip, colors);
|
||||
|
||||
return (
|
||||
<Cell
|
||||
@@ -335,14 +347,11 @@ export function TableNG(props: TableNGProps) {
|
||||
const renderCellContent = (props: RenderCellProps<TableRow, TableSummaryRow>): JSX.Element => {
|
||||
const rowIdx = props.row.__index;
|
||||
const value = props.row[props.column.key];
|
||||
|
||||
// TODO: defer until click?
|
||||
const actions = getActions?.(frame, field, props.row.__index, replaceVariables);
|
||||
const frame = data;
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderFieldCell({
|
||||
actions,
|
||||
cellOptions,
|
||||
frame,
|
||||
field,
|
||||
@@ -354,6 +363,7 @@ export function TableNG(props: TableNGProps) {
|
||||
width,
|
||||
cellInspect,
|
||||
showFilters,
|
||||
getActions: getCellActions,
|
||||
})}
|
||||
{showActions && (
|
||||
<TableCellActions
|
||||
@@ -504,12 +514,10 @@ export function TableNG(props: TableNGProps) {
|
||||
expandedRows,
|
||||
filter,
|
||||
footerCalcs,
|
||||
getActions,
|
||||
hasNestedFrames,
|
||||
isCountRowsSet,
|
||||
onCellFilterAdded,
|
||||
panelContext,
|
||||
replaceVariables,
|
||||
rowHeight,
|
||||
rows,
|
||||
setFilter,
|
||||
@@ -519,6 +527,7 @@ export function TableNG(props: TableNGProps) {
|
||||
theme,
|
||||
visibleFields,
|
||||
widths,
|
||||
getCellActions,
|
||||
]);
|
||||
|
||||
// invalidate columns on every structureRev change. this supports width editing in the fieldConfig.
|
||||
@@ -534,6 +543,8 @@ export function TableNG(props: TableNGProps) {
|
||||
return cellRootRenderers[props.column.key](key, props);
|
||||
};
|
||||
|
||||
const [tooltipState, setTooltipState] = useState<DataLinksActionsTooltipState>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataGrid<TableRow, TableSummaryRow>
|
||||
@@ -541,6 +552,24 @@ export function TableNG(props: TableNGProps) {
|
||||
className={styles.grid}
|
||||
columns={structureRevColumns}
|
||||
rows={paginatedRows}
|
||||
onCellClick={({ column, row }, { clientX, clientY, preventGridDefault }) => {
|
||||
// Note: could be column.field; JS says yes, but TS says no!
|
||||
const field = columns[column.idx].field;
|
||||
|
||||
if (colsWithTooltip[getDisplayName(field)]) {
|
||||
const rowIdx = row.__index;
|
||||
setTooltipState({
|
||||
coords: {
|
||||
clientX,
|
||||
clientY,
|
||||
},
|
||||
links: getCellLinks(field, rowIdx),
|
||||
actions: getCellActions(field, rowIdx),
|
||||
});
|
||||
|
||||
preventGridDefault();
|
||||
}
|
||||
}}
|
||||
onCellKeyDown={
|
||||
hasNestedFrames
|
||||
? (_, event) => {
|
||||
@@ -577,6 +606,15 @@ export function TableNG(props: TableNGProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tooltipState && (
|
||||
<DataLinksActionsTooltip
|
||||
links={tooltipState.links ?? []}
|
||||
actions={tooltipState.actions}
|
||||
coords={tooltipState.coords}
|
||||
onTooltipClose={() => setTooltipState(undefined)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isContextMenuOpen && (
|
||||
<ContextMenu
|
||||
x={contextMenuProps?.left || 0}
|
||||
@@ -770,31 +808,35 @@ const getCellStyles = (
|
||||
rowHeight: number,
|
||||
shouldWrap: boolean,
|
||||
shouldOverflow: boolean,
|
||||
hasTooltip: boolean,
|
||||
colors: CellColors
|
||||
) => ({
|
||||
cell: css({
|
||||
textOverflow: 'initial',
|
||||
background: colors.bgColor ?? 'inherit',
|
||||
alignContent: 'center',
|
||||
justifyContent: getTextAlign(field),
|
||||
paddingInline: TABLE.CELL_PADDING,
|
||||
height: '100%',
|
||||
minHeight: rowHeight, // min height interacts with the fit-content property on the overflow container
|
||||
...(shouldWrap && { whiteSpace: 'pre-line' }),
|
||||
'&:last-child': {
|
||||
borderInlineEnd: 'none',
|
||||
},
|
||||
'&:hover': {
|
||||
background: colors.bgHoverColor,
|
||||
'.table-cell-actions': {
|
||||
display: 'flex',
|
||||
) => {
|
||||
return {
|
||||
cell: css({
|
||||
textOverflow: 'initial',
|
||||
background: colors.bgColor ?? 'inherit',
|
||||
alignContent: 'center',
|
||||
justifyContent: getTextAlign(field),
|
||||
paddingInline: TABLE.CELL_PADDING,
|
||||
height: '100%',
|
||||
minHeight: rowHeight, // min height interacts with the fit-content property on the overflow container
|
||||
...(shouldWrap && { whiteSpace: 'pre-line' }),
|
||||
...(hasTooltip && { cursor: 'pointer' }),
|
||||
'&:last-child': {
|
||||
borderInlineEnd: 'none',
|
||||
},
|
||||
...(shouldOverflow && {
|
||||
zIndex: theme.zIndex.tooltip - 2,
|
||||
whiteSpace: 'pre-line',
|
||||
height: 'fit-content',
|
||||
minWidth: 'fit-content',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
});
|
||||
'&:hover': {
|
||||
background: colors.bgHoverColor,
|
||||
'.table-cell-actions': {
|
||||
display: 'flex',
|
||||
},
|
||||
...(shouldOverflow && {
|
||||
zIndex: theme.zIndex.tooltip - 2,
|
||||
whiteSpace: 'pre-line',
|
||||
height: 'fit-content',
|
||||
minWidth: 'fit-content',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useMemo, useEffect, useCallback, useRef, useLayoutEffect } fr
|
||||
import { Column, DataGridProps, SortColumn } from 'react-data-grid';
|
||||
import { varPreLine } from 'uwrap';
|
||||
|
||||
import { Field, fieldReducers, FieldType, formattedValueToString, reduceField } from '@grafana/data';
|
||||
import { Field, fieldReducers, FieldType, formattedValueToString, LinkModel, reduceField } from '@grafana/data';
|
||||
|
||||
import { useTheme2 } from '../../../themes/ThemeContext';
|
||||
import { TableCellDisplayMode, TableColumnResizeActionCallback } from '../types';
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getColumnTypes,
|
||||
GetMaxWrapCellOptions,
|
||||
getMaxWrapCell,
|
||||
getCellLinks,
|
||||
} from './utils';
|
||||
|
||||
// Helper function to get displayed value
|
||||
@@ -597,3 +598,10 @@ export function useColumnResize(
|
||||
|
||||
return dataGridResizeHandler;
|
||||
}
|
||||
|
||||
export function useSingleLink(field: Field, rowIdx: number): LinkModel | undefined {
|
||||
const linksCount = field.config.links?.length ?? 0;
|
||||
const actionsCount = field.config.actions?.length ?? 0;
|
||||
const shouldShowLink = linksCount === 1 && actionsCount === 0;
|
||||
return useMemo(() => (shouldShowLink ? (getCellLinks(field, rowIdx) ?? []) : [])[0], [field, shouldShowLink, rowIdx]);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TimeRange,
|
||||
FieldConfigSource,
|
||||
ActionModel,
|
||||
InterpolateFunction,
|
||||
FieldType,
|
||||
DataFrameWithValue,
|
||||
SelectableValue,
|
||||
@@ -30,12 +29,9 @@ export type TableColumnResizeActionCallback = (fieldDisplayName: string, width:
|
||||
export type TableSortByActionCallback = (state: TableSortByFieldState[]) => void;
|
||||
export type FooterItem = Array<KeyValue<string>> | string | undefined;
|
||||
|
||||
export type GetActionsFunction = (
|
||||
frame: DataFrame,
|
||||
field: Field,
|
||||
rowIndex: number,
|
||||
replaceVariables?: InterpolateFunction
|
||||
) => ActionModel[];
|
||||
export type GetActionsFunction = (frame: DataFrame, field: Field, rowIndex: number) => ActionModel[];
|
||||
|
||||
export type GetActionsFunctionLocal = (field: Field, rowIndex: number) => ActionModel[];
|
||||
|
||||
export type TableFieldOptionsType = Omit<TableFieldOptions, 'cellOptions'> & {
|
||||
cellOptions: TableCellOptions;
|
||||
@@ -142,7 +138,6 @@ export interface BaseTableProps {
|
||||
initialRowIndex?: number;
|
||||
fieldConfig?: FieldConfigSource;
|
||||
getActions?: GetActionsFunction;
|
||||
replaceVariables?: InterpolateFunction;
|
||||
// Used solely for testing as RTL can't correctly render the table otherwise
|
||||
enableVirtualization?: boolean;
|
||||
}
|
||||
@@ -151,7 +146,6 @@ export interface BaseTableProps {
|
||||
export interface TableNGProps extends BaseTableProps {}
|
||||
|
||||
export interface TableCellRendererProps {
|
||||
actions?: ActionModel[];
|
||||
rowIdx: number;
|
||||
frame: DataFrame;
|
||||
timeRange?: TimeRange;
|
||||
@@ -165,6 +159,7 @@ export interface TableCellRendererProps {
|
||||
cellInspect: boolean;
|
||||
showFilters: boolean;
|
||||
justifyContent: Property.JustifyContent;
|
||||
getActions?: GetActionsFunctionLocal;
|
||||
}
|
||||
|
||||
export type ContextMenuProps = {
|
||||
@@ -205,7 +200,7 @@ export interface SparklineCellProps {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface BarGaugeCellProps extends ActionCellProps {
|
||||
export interface BarGaugeCellProps {
|
||||
field: Field;
|
||||
height: number;
|
||||
rowIdx: number;
|
||||
@@ -214,7 +209,7 @@ export interface BarGaugeCellProps extends ActionCellProps {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface ImageCellProps extends ActionCellProps {
|
||||
export interface ImageCellProps {
|
||||
cellOptions: TableCellOptions;
|
||||
field: Field;
|
||||
height: number;
|
||||
@@ -223,7 +218,7 @@ export interface ImageCellProps extends ActionCellProps {
|
||||
rowIdx: number;
|
||||
}
|
||||
|
||||
export interface JSONCellProps extends ActionCellProps {
|
||||
export interface JSONCellProps {
|
||||
justifyContent: Property.JustifyContent;
|
||||
value: TableCellValue;
|
||||
field: Field;
|
||||
@@ -241,24 +236,26 @@ export interface GeoCellProps {
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface ActionCellProps {
|
||||
actions?: ActionModel[];
|
||||
}
|
||||
|
||||
export interface CellColors {
|
||||
textColor?: string;
|
||||
bgColor?: string;
|
||||
bgHoverColor?: string;
|
||||
}
|
||||
|
||||
export interface AutoCellProps extends ActionCellProps {
|
||||
value: TableCellValue;
|
||||
export interface AutoCellProps {
|
||||
field: Field;
|
||||
value: TableCellValue;
|
||||
justifyContent: Property.JustifyContent;
|
||||
rowIdx: number;
|
||||
cellOptions: TableCellOptions;
|
||||
}
|
||||
|
||||
export interface ActionCellProps {
|
||||
field: Field;
|
||||
rowIdx: number;
|
||||
getActions: GetActionsFunctionLocal;
|
||||
}
|
||||
|
||||
// Comparator for sorting table values
|
||||
export type Comparator = (a: TableCellValue, b: TableCellValue) => number;
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@ const DEFAULT_CELL_OPTIONS = { type: TableCellDisplayMode.Auto } as const;
|
||||
/**
|
||||
* @internal
|
||||
* Returns the cell options for a field, migrating from legacy displayMode if necessary.
|
||||
* TODO: remove live migration in favor of doing it in dashboard or panel migrator
|
||||
*/
|
||||
export function getCellOptions(field: Field): TableCellOptions {
|
||||
if (field.config.custom?.displayMode) {
|
||||
@@ -613,3 +614,12 @@ export function getApplyToRowBgFn(fields: Field[], theme: GrafanaTheme2): ((rowI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function withDataLinksActionsTooltip(field: Field, cellType: TableCellDisplayMode) {
|
||||
return (
|
||||
cellType !== TableCellDisplayMode.DataLinks &&
|
||||
cellType !== TableCellDisplayMode.Actions &&
|
||||
(field.config.links?.length ?? 0) + (field.config.actions?.length ?? 0) > 1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface TableCellProps extends CellProps<any> {
|
||||
onCellFilterAdded?: TableFilterActionCallback;
|
||||
innerWidth: number;
|
||||
frame: DataFrame;
|
||||
actions?: ActionModel[];
|
||||
actions?: ActionModel[]; // unused in NG
|
||||
setInspectCell?: TableInspectCellCallback;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,8 @@ export function getCellComponent(displayMode: TableCellDisplayMode, field: Field
|
||||
return DataLinksCell;
|
||||
case TableCellDisplayMode.Actions:
|
||||
return ActionsCell;
|
||||
case TableCellDisplayMode.Pill:
|
||||
return DefaultCell; // Legacy table doesn't support pill cells, fallback to default
|
||||
}
|
||||
|
||||
if (field.type === FieldType.geo) {
|
||||
@@ -764,10 +766,16 @@ export function guessLongestField(fieldConfig: FieldConfigSource, data: DataFram
|
||||
return longestField;
|
||||
}
|
||||
|
||||
export type DataLinksActionsTooltipCoords = {
|
||||
export interface DataLinksActionsTooltipState {
|
||||
coords: DataLinksActionsTooltipCoords;
|
||||
links?: LinkModel[];
|
||||
actions?: ActionModel[];
|
||||
}
|
||||
|
||||
export interface DataLinksActionsTooltipCoords {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const getDataLinksActionsTooltipUtils = (links: LinkModel[], actions?: ActionModel[]) => {
|
||||
const hasMultipleLinksOrActions = links.length > 1 || Boolean(actions?.length);
|
||||
|
||||
@@ -245,78 +245,136 @@ func (s *SocialGenericOAuth) UserInfo(ctx context.Context, client *http.Client,
|
||||
defer s.reloadMutex.RUnlock()
|
||||
|
||||
s.log.Debug("Getting user info")
|
||||
toCheck := make([]*UserInfoJson, 0, 2)
|
||||
|
||||
if tokenData := s.extractFromToken(token); tokenData != nil {
|
||||
toCheck = append(toCheck, tokenData)
|
||||
// 1. Collect user info data from various sources
|
||||
dataSources := s.collectUserInfoData(ctx, client, token)
|
||||
|
||||
// 2. Build user info from collected data
|
||||
userInfo, externalOrgs, err := s.buildUserInfo(dataSources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3. Post-process user info
|
||||
err = s.postProcessUserInfo(ctx, client, userInfo, externalOrgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. Validate user access
|
||||
err = s.validateUserAccess(ctx, client, userInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.log.Debug("User info result", "result", userInfo)
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// collectUserInfoData gathers user information from ID token, API, and access token
|
||||
func (s *SocialGenericOAuth) collectUserInfoData(ctx context.Context, client *http.Client, token *oauth2.Token) []*UserInfoJson {
|
||||
dataSources := make([]*UserInfoJson, 0, 3)
|
||||
|
||||
if idTokenData := s.extractFromIDToken(token); idTokenData != nil {
|
||||
dataSources = append(dataSources, idTokenData)
|
||||
}
|
||||
if apiData := s.extractFromAPI(ctx, client); apiData != nil {
|
||||
toCheck = append(toCheck, apiData)
|
||||
dataSources = append(dataSources, apiData)
|
||||
}
|
||||
if accessTokenData := s.extractFromAccessToken(token); accessTokenData != nil {
|
||||
dataSources = append(dataSources, accessTokenData)
|
||||
}
|
||||
|
||||
return dataSources
|
||||
}
|
||||
|
||||
// buildUserInfo constructs BasicUserInfo from collected data sources
|
||||
func (s *SocialGenericOAuth) buildUserInfo(dataSources []*UserInfoJson) (*social.BasicUserInfo, []string, error) {
|
||||
userInfo := &social.BasicUserInfo{}
|
||||
var externalOrgs []string
|
||||
for _, data := range toCheck {
|
||||
|
||||
for _, data := range dataSources {
|
||||
s.log.Debug("Processing external user info", "source", data.source, "data", data)
|
||||
|
||||
if userInfo.Id == "" {
|
||||
userInfo.Id = data.Sub
|
||||
s.extractBasicUserFields(userInfo, data)
|
||||
|
||||
if err := s.extractRoleAndOrgs(userInfo, &externalOrgs, data); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if userInfo.Name == "" {
|
||||
userInfo.Name = s.extractUserName(data)
|
||||
}
|
||||
s.extractUserGroups(userInfo, data)
|
||||
}
|
||||
|
||||
if userInfo.Login == "" {
|
||||
userInfo.Login = s.extractLogin(data)
|
||||
}
|
||||
return userInfo, externalOrgs, nil
|
||||
}
|
||||
|
||||
if userInfo.Email == "" {
|
||||
userInfo.Email = s.extractEmail(data)
|
||||
if userInfo.Email != "" {
|
||||
s.log.Debug("Set user info email from extracted email", "email", userInfo.Email)
|
||||
}
|
||||
}
|
||||
// extractBasicUserFields extracts basic user fields (ID, Name, Login, Email) from data
|
||||
func (s *SocialGenericOAuth) extractBasicUserFields(userInfo *social.BasicUserInfo, data *UserInfoJson) {
|
||||
if userInfo.Id == "" {
|
||||
userInfo.Id = data.Sub
|
||||
}
|
||||
|
||||
if userInfo.Role == "" && !s.info.SkipOrgRoleSync {
|
||||
role, grafanaAdmin, err := s.extractRoleAndAdminOptional(data.rawJSON, []string{})
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract role", "err", err)
|
||||
} else {
|
||||
userInfo.Role = role
|
||||
if s.info.AllowAssignGrafanaAdmin {
|
||||
userInfo.IsGrafanaAdmin = &grafanaAdmin
|
||||
}
|
||||
}
|
||||
}
|
||||
if userInfo.Name == "" {
|
||||
userInfo.Name = s.extractUserName(data)
|
||||
}
|
||||
|
||||
if len(externalOrgs) == 0 && !s.info.SkipOrgRoleSync {
|
||||
var err error
|
||||
externalOrgs, err = s.extractOrgs(data.rawJSON)
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract orgs", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if userInfo.Login == "" {
|
||||
userInfo.Login = s.extractLogin(data)
|
||||
}
|
||||
|
||||
if len(userInfo.Groups) == 0 {
|
||||
groups, err := s.extractGroups(data)
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract groups", "err", err)
|
||||
} else if len(groups) > 0 {
|
||||
s.log.Debug("Setting user info groups from extracted groups")
|
||||
userInfo.Groups = groups
|
||||
if userInfo.Email == "" {
|
||||
userInfo.Email = s.extractEmail(data)
|
||||
if userInfo.Email != "" {
|
||||
s.log.Debug("Set user info email from extracted email", "email", userInfo.Email)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extractRoleAndOrgs extracts role and organization information from data
|
||||
func (s *SocialGenericOAuth) extractRoleAndOrgs(userInfo *social.BasicUserInfo, externalOrgs *[]string, data *UserInfoJson) error {
|
||||
if userInfo.Role == "" && !s.info.SkipOrgRoleSync {
|
||||
role, grafanaAdmin, err := s.extractRoleAndAdminOptional(data.rawJSON, []string{})
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract role", "err", err)
|
||||
} else {
|
||||
userInfo.Role = role
|
||||
if s.info.AllowAssignGrafanaAdmin {
|
||||
userInfo.IsGrafanaAdmin = &grafanaAdmin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(*externalOrgs) == 0 && !s.info.SkipOrgRoleSync {
|
||||
orgs, err := s.extractOrgs(data.rawJSON)
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract orgs", "err", err)
|
||||
return err
|
||||
}
|
||||
*externalOrgs = orgs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractUserGroups extracts group information from data
|
||||
func (s *SocialGenericOAuth) extractUserGroups(userInfo *social.BasicUserInfo, data *UserInfoJson) {
|
||||
if len(userInfo.Groups) == 0 {
|
||||
groups, err := s.extractGroups(data)
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract groups", "err", err)
|
||||
} else if len(groups) > 0 {
|
||||
s.log.Debug("Setting user info groups from extracted groups")
|
||||
userInfo.Groups = groups
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// postProcessUserInfo handles post-processing of user info (org roles, private email, etc.)
|
||||
func (s *SocialGenericOAuth) postProcessUserInfo(ctx context.Context, client *http.Client, userInfo *social.BasicUserInfo, externalOrgs []string) error {
|
||||
if !s.info.SkipOrgRoleSync {
|
||||
userInfo.OrgRoles = s.orgRoleMapper.MapOrgRoles(s.orgMappingCfg, externalOrgs, userInfo.Role)
|
||||
if s.info.RoleAttributeStrict && len(userInfo.OrgRoles) == 0 {
|
||||
// If no roles are found and role_attribute_strict is set, return an error.
|
||||
// The s.info.RoleAttributeStrict is necessary, because there is a case when len(userInfo.OrgRoles) == 0,
|
||||
// but strict role mapping is not enabled (when getAllOrgs fails).
|
||||
return nil, errRoleAttributeStrictViolation.Errorf("could not evaluate any valid roles using IdP provided data")
|
||||
return errRoleAttributeStrictViolation.Errorf("could not evaluate any valid roles using IdP provided data")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,11 +383,11 @@ func (s *SocialGenericOAuth) UserInfo(ctx context.Context, client *http.Client,
|
||||
}
|
||||
|
||||
if s.canFetchPrivateEmail(userInfo) {
|
||||
var err error
|
||||
userInfo.Email, err = s.fetchPrivateEmail(ctx, client)
|
||||
email, err := s.fetchPrivateEmail(ctx, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
userInfo.Email = email
|
||||
s.log.Debug("Setting email from fetched private email", "email", userInfo.Email)
|
||||
}
|
||||
|
||||
@@ -338,28 +396,32 @@ func (s *SocialGenericOAuth) UserInfo(ctx context.Context, client *http.Client,
|
||||
userInfo.Login = userInfo.Email
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateUserAccess validates user access based on team, organization, and group membership
|
||||
func (s *SocialGenericOAuth) validateUserAccess(ctx context.Context, client *http.Client, userInfo *social.BasicUserInfo) error {
|
||||
if !s.isTeamMember(ctx, client) {
|
||||
return nil, &SocialError{"User not a member of one of the required teams"}
|
||||
return &SocialError{"User not a member of one of the required teams"}
|
||||
}
|
||||
|
||||
if !s.isOrganizationMember(ctx, client) {
|
||||
return nil, &SocialError{"User not a member of one of the required organizations"}
|
||||
return &SocialError{"User not a member of one of the required organizations"}
|
||||
}
|
||||
|
||||
if !s.isGroupMember(userInfo.Groups) {
|
||||
return nil, errMissingGroupMembership
|
||||
return errMissingGroupMembership
|
||||
}
|
||||
|
||||
s.log.Debug("User info result", "result", userInfo)
|
||||
return userInfo, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SocialGenericOAuth) canFetchPrivateEmail(userinfo *social.BasicUserInfo) bool {
|
||||
return s.info.ApiUrl != "" && userinfo.Email == ""
|
||||
}
|
||||
|
||||
func (s *SocialGenericOAuth) extractFromToken(token *oauth2.Token) *UserInfoJson {
|
||||
s.log.Debug("Extracting user info from OAuth token")
|
||||
func (s *SocialGenericOAuth) extractFromIDToken(token *oauth2.Token) *UserInfoJson {
|
||||
s.log.Debug("Extracting user info from OAuth ID token")
|
||||
|
||||
idTokenAttribute := "id_token"
|
||||
if s.idTokenAttributeName != "" {
|
||||
@@ -373,21 +435,44 @@ func (s *SocialGenericOAuth) extractFromToken(token *oauth2.Token) *UserInfoJson
|
||||
return nil
|
||||
}
|
||||
|
||||
rawJSON, err := s.retrieveRawIDToken(idToken)
|
||||
rawJSON, err := s.retrieveRawJWTPayload(idToken)
|
||||
if err != nil {
|
||||
s.log.Warn("Error retrieving id_token", "error", err, "token", fmt.Sprintf("%+v", token))
|
||||
s.log.Warn("Error retrieving id_token payload", "error", err, "token", fmt.Sprintf("%+v", token))
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.parseUserInfoFromJSON(rawJSON, "id_token")
|
||||
}
|
||||
|
||||
func (s *SocialGenericOAuth) extractFromAccessToken(token *oauth2.Token) *UserInfoJson {
|
||||
s.log.Debug("Extracting user info from OAuth access token")
|
||||
|
||||
accessToken := token.AccessToken
|
||||
if accessToken == "" {
|
||||
s.log.Debug("No access token found")
|
||||
return nil
|
||||
}
|
||||
|
||||
rawJSON, err := s.retrieveRawJWTPayload(accessToken)
|
||||
if err != nil {
|
||||
s.log.Warn("Error retrieving access token payload", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.parseUserInfoFromJSON(rawJSON, "access_token")
|
||||
}
|
||||
|
||||
// parseUserInfoFromJSON is a helper method to parse UserInfoJson from raw JSON and source
|
||||
func (s *SocialGenericOAuth) parseUserInfoFromJSON(rawJSON []byte, source string) *UserInfoJson {
|
||||
var data UserInfoJson
|
||||
if err := json.Unmarshal(rawJSON, &data); err != nil {
|
||||
s.log.Error("Error decoding id_token JSON", "raw_json", string(rawJSON), "error", err)
|
||||
s.log.Error("Error decoding user info JSON", "raw_json", string(rawJSON), "error", err, "source", source)
|
||||
return nil
|
||||
}
|
||||
|
||||
data.rawJSON = rawJSON
|
||||
data.source = "token"
|
||||
s.log.Debug("Received id_token", "raw_json", string(data.rawJSON), "data", data.String())
|
||||
data.source = source
|
||||
s.log.Debug("Parsed user info from JSON", "raw_json", string(rawJSON), "data", data.String(), "source", source)
|
||||
return &data
|
||||
}
|
||||
|
||||
@@ -404,18 +489,7 @@ func (s *SocialGenericOAuth) extractFromAPI(ctx context.Context, client *http.Cl
|
||||
return nil
|
||||
}
|
||||
|
||||
rawJSON := rawUserInfoResponse.Body
|
||||
|
||||
var data UserInfoJson
|
||||
if err := json.Unmarshal(rawJSON, &data); err != nil {
|
||||
s.log.Error("Error decoding user info response", "raw_json", rawJSON, "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
data.rawJSON = rawJSON
|
||||
data.source = "API"
|
||||
s.log.Debug("Received user info response from API", "raw_json", string(rawJSON), "data", data.String())
|
||||
return &data
|
||||
return s.parseUserInfoFromJSON(rawUserInfoResponse.Body, "API")
|
||||
}
|
||||
|
||||
func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string {
|
||||
|
||||
@@ -31,6 +31,7 @@ func TestUserInfoSearchesForEmailAndOrgRoles(t *testing.T) {
|
||||
AllowAssignGrafanaAdmin bool
|
||||
ResponseBody any
|
||||
OAuth2Extra any
|
||||
AccessToken string
|
||||
Setup func(*orgtest.FakeOrgService)
|
||||
RoleAttributePath string
|
||||
RoleAttributeStrict bool
|
||||
@@ -440,6 +441,62 @@ func TestUserInfoSearchesForEmailAndOrgRoles(t *testing.T) {
|
||||
ExpectedEmail: "john.doe@example.com",
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{2: org.RoleViewer},
|
||||
},
|
||||
// Access Token Test Cases
|
||||
{
|
||||
Name: "Given a valid access token with role, no ID token, no API response, use access token",
|
||||
ResponseBody: map[string]any{},
|
||||
OAuth2Extra: map[string]any{},
|
||||
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiRWRpdG9yIiwiZW1haWwiOiJhY2Nlc3MudG9rZW5AZXhhbXBsZS5jb20ifQ.oVEMSJVqBwrGXOcwGgXL_8J-CZhgFVPjXXSqzPJQ5JU", // { "role": "Editor", "email": "access.token@example.com" }
|
||||
RoleAttributePath: "role",
|
||||
ExpectedEmail: "access.token@example.com",
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{2: org.RoleEditor},
|
||||
},
|
||||
{
|
||||
Name: "Given a valid access token with org roles, no ID token, no API response, use access token",
|
||||
ResponseBody: map[string]any{},
|
||||
OAuth2Extra: map[string]any{},
|
||||
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiVmlld2VyIiwiZW1haWwiOiJhY2Nlc3MudG9rZW5AZXhhbXBsZS5jb20iLCJpbmZvIjp7InJvbGVzIjpbImFjY2Vzcy1kZXYiLCJhY2Nlc3Mtb3BzIl19fQ.g8-mNJQDL9CJWgRTFdKBRRKbsHZfFhJrzPYQGXfxGIE", // { "role": "Viewer", "email": "access.token@example.com", "info": { "roles": [ "access-dev", "access-ops" ] }}
|
||||
RoleAttributePath: "role",
|
||||
OrgAttributePath: "info.roles",
|
||||
OrgMapping: []string{"access-dev:org_dev:Admin", "access-ops:org_engineering:Editor"},
|
||||
ExpectedEmail: "access.token@example.com",
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{4: org.RoleAdmin, 5: org.RoleEditor},
|
||||
},
|
||||
{
|
||||
Name: "Given a valid access token and ID token, prefer ID token",
|
||||
ResponseBody: map[string]any{},
|
||||
OAuth2Extra: map[string]any{
|
||||
// { "role": "Admin", "email": "id.token@example.com" }
|
||||
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6ImlkLnRva2VuQGV4YW1wbGUuY29tIn0.T8wcoOOPQ_av9VsOFoYJZGNFGJgG0d3LPDvtxvgODkU",
|
||||
},
|
||||
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiRWRpdG9yIiwiZW1haWwiOiJhY2Nlc3MudG9rZW5AZXhhbXBsZS5jb20ifQ.oVEMSJVqBwrGXOcwGgXL_8J-CZhgFVPjXXSqzPJQ5JU", // { "role": "Editor", "email": "access.token@example.com" }
|
||||
RoleAttributePath: "role",
|
||||
ExpectedEmail: "id.token@example.com",
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{2: org.RoleAdmin},
|
||||
},
|
||||
{
|
||||
Name: "Given a valid access token with no email, ID token with no role, API response with no data, merge",
|
||||
ResponseBody: map[string]any{},
|
||||
OAuth2Extra: map[string]any{
|
||||
// { "email": "id.token@example.com" }
|
||||
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImlkLnRva2VuQGV4YW1wbGUuY29tIn0.k5GwPcZvGe2BE_jgwN0ntz0nz4KlYhEd0hRRLApkTJ4",
|
||||
},
|
||||
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiRWRpdG9yIn0.gfnKWZKNFNqrILhHFzabBVEWnJJIZBmQSBwLPCHhLUY", // { "role": "Editor" }
|
||||
RoleAttributePath: "role",
|
||||
ExpectedEmail: "id.token@example.com",
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{2: org.RoleEditor},
|
||||
},
|
||||
{
|
||||
Name: "Given a valid access token with GrafanaAdmin role and AssignGrafanaAdmin enabled",
|
||||
AllowAssignGrafanaAdmin: true,
|
||||
ResponseBody: map[string]any{},
|
||||
OAuth2Extra: map[string]any{},
|
||||
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiR3JhZmFuYUFkbWluIiwiZW1haWwiOiJhY2Nlc3MudG9rZW5AZXhhbXBsZS5jb20ifQ.fJPjMgZW9bOYXOLgOUekNQmNrVbUNhU1iqQJwqFWzUY", // { "role": "GrafanaAdmin", "email": "access.token@example.com" }
|
||||
RoleAttributePath: "role",
|
||||
ExpectedEmail: "access.token@example.com",
|
||||
ExpectedGrafanaAdmin: trueBoolPtr(),
|
||||
ExpectedOrgRoles: map[int64]org.RoleType{2: org.RoleAdmin},
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
@@ -479,8 +536,9 @@ func TestUserInfoSearchesForEmailAndOrgRoles(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
provider.info.ApiUrl = ts.URL
|
||||
|
||||
staticToken := oauth2.Token{
|
||||
AccessToken: "",
|
||||
AccessToken: tc.AccessToken,
|
||||
TokenType: "",
|
||||
RefreshToken: "",
|
||||
Expiry: time.Now(),
|
||||
@@ -853,7 +911,7 @@ func TestPayloadCompression(t *testing.T) {
|
||||
}
|
||||
|
||||
token := staticToken.WithExtra(test.OAuth2Extra)
|
||||
userInfo := provider.extractFromToken(token)
|
||||
userInfo := provider.extractFromIDToken(token)
|
||||
|
||||
if test.ExpectedEmail == "" {
|
||||
require.Nil(t, userInfo, "Testing case %q", test.Name)
|
||||
|
||||
@@ -275,7 +275,7 @@ func (s *SocialGitlab) extractFromToken(ctx context.Context, client *http.Client
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rawJSON, err := s.retrieveRawIDToken(idToken)
|
||||
rawJSON, err := s.retrieveRawJWTPayload(idToken)
|
||||
if err != nil {
|
||||
s.log.Warn("Error retrieving id_token", "error", err, "token", fmt.Sprintf("%+v", idToken))
|
||||
return nil, nil
|
||||
|
||||
@@ -236,7 +236,7 @@ func (s *SocialGoogle) extractFromToken(_ context.Context, _ *http.Client, token
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rawJSON, err := s.retrieveRawIDToken(idToken)
|
||||
rawJSON, err := s.retrieveRawJWTPayload(idToken)
|
||||
if err != nil {
|
||||
s.log.Warn("Error retrieving id_token", "error", err, "token", fmt.Sprintf("%+v", idToken))
|
||||
return nil, nil
|
||||
|
||||
@@ -196,21 +196,21 @@ func (s *SocialBase) isGroupMember(groups []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SocialBase) retrieveRawIDToken(idToken any) ([]byte, error) {
|
||||
tokenString, ok := idToken.(string)
|
||||
func (s *SocialBase) retrieveRawJWTPayload(token any) ([]byte, error) {
|
||||
tokenString, ok := token.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("id_token is not a string: %v", idToken)
|
||||
return nil, fmt.Errorf("token is not a string: %v", token)
|
||||
}
|
||||
|
||||
jwtRegexp := regexp.MustCompile("^([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)$")
|
||||
matched := jwtRegexp.FindStringSubmatch(tokenString)
|
||||
if matched == nil {
|
||||
return nil, fmt.Errorf("id_token is not in JWT format: %s", tokenString)
|
||||
return nil, fmt.Errorf("token is not in JWT format: %s", tokenString)
|
||||
}
|
||||
|
||||
rawJSON, err := base64.RawURLEncoding.DecodeString(matched[2])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error base64 decoding id_token: %w", err)
|
||||
return nil, fmt.Errorf("error base64 decoding token payload: %w", err)
|
||||
}
|
||||
|
||||
headerBytes, err := base64.RawURLEncoding.DecodeString(matched[1])
|
||||
|
||||
@@ -114,9 +114,6 @@ func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identi
|
||||
|
||||
if !s.cfg.JWTAuth.SkipOrgRoleSync {
|
||||
role, grafanaAdmin := s.extractRoleAndAdmin(claims)
|
||||
if err != nil {
|
||||
s.log.Warn("Failed to extract role", "err", err)
|
||||
}
|
||||
|
||||
if s.cfg.JWTAuth.AllowAssignGrafanaAdmin {
|
||||
id.IsGrafanaAdmin = &grafanaAdmin
|
||||
|
||||
@@ -861,7 +861,7 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestFoldersCreateAPIEndpointK8S is the counterpart of pkg/api/folder_test.go TestFoldersCreateAPIEndpoint
|
||||
func TestFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
@@ -902,7 +902,7 @@ func TestFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
description: "folder creation fails without permissions to create a folder",
|
||||
input: folderWithoutParentInput,
|
||||
expectedCode: http.StatusForbidden,
|
||||
expectedMessage: dashboards.ErrFolderAccessDenied.Error(),
|
||||
expectedMessage: fmt.Sprintf("You'll need additional permissions to perform this action. Permissions needed: %s", "folders:create"),
|
||||
permissions: []resourcepermissions.SetResourcePermissionCommand{},
|
||||
},
|
||||
{
|
||||
@@ -1022,7 +1022,7 @@ func testDescription(description string, expectedErr error) string {
|
||||
}
|
||||
|
||||
// There are no counterpart of TestFoldersGetAPIEndpointK8S in pkg/api/folder_test.go
|
||||
func TestFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
@@ -1062,6 +1062,7 @@ func TestFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
expectedOutput: []dtos.FolderSearchHit{
|
||||
{UID: "foo", Title: "Folder 1"},
|
||||
{UID: "qux", Title: "Folder 3"},
|
||||
{UID: folder.SharedWithMeFolder.UID, Title: folder.SharedWithMeFolder.Title},
|
||||
},
|
||||
permissions: folderReadAndCreatePermission,
|
||||
},
|
||||
@@ -1107,7 +1108,7 @@ func TestFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
}
|
||||
|
||||
// test on all dualwriter modes
|
||||
for mode := 1; mode <= 4; mode++ {
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
for _, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("Mode: %d, %s", mode, tc.description), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
@@ -1123,6 +1124,7 @@ func TestFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagNestedFolders,
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -210,7 +210,7 @@ export function NewProvisionedFolderForm({ parentFolder, onDismiss }: Props) {
|
||||
const { workflowOptions, isGitHub, repository, folder, initialValues } = useProvisionedFolderFormData({
|
||||
folderUid: parentFolder?.uid,
|
||||
action: 'create',
|
||||
title: parentFolder?.title,
|
||||
title: '', // Empty title for new folders
|
||||
});
|
||||
|
||||
if (!initialValues) {
|
||||
|
||||
+16
-11
@@ -18,6 +18,9 @@ import { addQuery } from 'app/core/utils/query';
|
||||
import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard';
|
||||
import { storeLastUsedDataSourceInLocalStorage } from 'app/features/datasources/components/picker/utils';
|
||||
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
||||
import { ExpressionTypeDropdown } from 'app/features/expressions/components/ExpressionTypeDropdown';
|
||||
import { ExpressionQueryType } from 'app/features/expressions/types';
|
||||
import { getDefaults } from 'app/features/expressions/utils/expressionTypes';
|
||||
import { GroupActionComponents } from 'app/features/query/components/QueryActionComponent';
|
||||
import { QueryEditorRows } from 'app/features/query/components/QueryEditorRows';
|
||||
import { QueryGroupTopSection } from 'app/features/query/components/QueryGroup';
|
||||
@@ -286,9 +289,15 @@ export class PanelDataQueriesTab extends SceneObjectBase<PanelDataQueriesTabStat
|
||||
return (dsSettings.meta.backend || dsSettings.meta.alerting || dsSettings.meta.mixed) === true;
|
||||
}
|
||||
|
||||
public onAddExpressionClick = () => {
|
||||
public onAddExpressionOfType = (type: ExpressionQueryType) => {
|
||||
const queries = this.getQueries();
|
||||
this.onQueriesChange(addQuery(queries, expressionDatasource.newQuery()));
|
||||
// Create base expression query with the specified type
|
||||
const baseQuery = expressionDatasource.newQuery();
|
||||
const queryWithType = { ...baseQuery, type };
|
||||
// Apply defaults specific to the expression type
|
||||
const queryWithDefaults = getDefaults(queryWithType);
|
||||
|
||||
this.onQueriesChange(addQuery(queries, queryWithDefaults));
|
||||
};
|
||||
|
||||
public renderExtraActions() {
|
||||
@@ -316,6 +325,7 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
|
||||
if (!datasource || !dsSettings || !data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showAddButton = !isSharedDashboardQuery(dsSettings.name);
|
||||
const onSelectQueryFromLibrary = async (query: DataQuery) => {
|
||||
// ensure all queries explicitly define a datasource
|
||||
@@ -394,16 +404,11 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
|
||||
</>
|
||||
)}
|
||||
{config.expressionsEnabled && model.isExpressionsSupported(dsSettings) && (
|
||||
<Button
|
||||
icon="plus"
|
||||
onClick={model.onAddExpressionClick}
|
||||
variant="secondary"
|
||||
data-testid={selectors.components.QueryTab.addExpression}
|
||||
>
|
||||
<span>
|
||||
<ExpressionTypeDropdown handleOnSelect={model.onAddExpressionOfType}>
|
||||
<Button icon="plus" variant="secondary" data-testid={selectors.components.QueryTab.addExpression}>
|
||||
<Trans i18nKey="dashboard-scene.panel-data-queries-tab-rendered.expression">Expression </Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</Button>
|
||||
</ExpressionTypeDropdown>
|
||||
)}
|
||||
{model.renderExtraActions()}
|
||||
</Stack>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { DataSourceApi, QueryEditorProps, SelectableValue } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { InlineField, Select } from '@grafana/ui';
|
||||
import { DataSourceApi, GrafanaTheme2, QueryEditorProps } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { Button, IconButton, InlineField, PopoverContent, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { ClassicConditions } from './components/ClassicConditions';
|
||||
import { ExpressionTypeDropdown } from './components/ExpressionTypeDropdown';
|
||||
import { Math } from './components/Math';
|
||||
import { Reduce } from './components/Reduce';
|
||||
import { Resample } from './components/Resample';
|
||||
@@ -20,6 +22,24 @@ const labelWidth = 15;
|
||||
type NonClassicExpressionType = Exclude<ExpressionQueryType, ExpressionQueryType.classic>;
|
||||
type ExpressionTypeConfigStorage = Partial<Record<NonClassicExpressionType, string>>;
|
||||
|
||||
// Help text for each expression type - can be expanded with more detailed content
|
||||
const getExpressionHelpText = (type: ExpressionQueryType): PopoverContent | string => {
|
||||
const description = expressionTypes.find(({ value }) => value === type)?.description;
|
||||
|
||||
switch (type) {
|
||||
case ExpressionQueryType.sql:
|
||||
return (
|
||||
<Trans i18nKey="expressions.expression-query-editor.helper-text-sql">
|
||||
Run MySQL-dialect SQL against the tables returned from your data sources. Data source queries (ie "A", "B")
|
||||
are available as tables and referenced by query-name. Fields are available as columns, as returned from the
|
||||
data source.
|
||||
</Trans>
|
||||
);
|
||||
default:
|
||||
return description ?? '';
|
||||
}
|
||||
};
|
||||
|
||||
function useExpressionsCache() {
|
||||
const expressionCache = useRef<ExpressionTypeConfigStorage>({});
|
||||
|
||||
@@ -62,14 +82,16 @@ export function ExpressionQueryEditor(props: Props) {
|
||||
const { query, queries, onRunQuery, onChange, app } = props;
|
||||
const { getCachedExpression, setCachedExpression } = useExpressionsCache();
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
useEffect(() => {
|
||||
setCachedExpression(query.type, query.expression);
|
||||
}, [query.expression, query.type, setCachedExpression]);
|
||||
|
||||
const onSelectExpressionType = useCallback(
|
||||
(item: SelectableValue<ExpressionQueryType>) => {
|
||||
const cachedExpression = getCachedExpression(item.value!);
|
||||
const defaults = getDefaults({ ...query, type: item.value! });
|
||||
(value: ExpressionQueryType) => {
|
||||
const cachedExpression = getCachedExpression(value!);
|
||||
const defaults = getDefaults({ ...query, type: value! });
|
||||
|
||||
onChange({ ...defaults, expression: cachedExpression ?? defaults.expression });
|
||||
},
|
||||
@@ -100,17 +122,35 @@ export function ExpressionQueryEditor(props: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
const selected = expressionTypes.find((o) => o.value === query.type);
|
||||
const helperText = getExpressionHelpText(query.type);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<InlineField
|
||||
label={t('expressions.expression-query-editor.label-operation', 'Operation')}
|
||||
labelWidth={labelWidth}
|
||||
>
|
||||
<Select options={expressionTypes} value={selected} onChange={onSelectExpressionType} width={25} />
|
||||
</InlineField>
|
||||
<div className={styles.operationRow}>
|
||||
<InlineField
|
||||
label={t('expressions.expression-query-editor.label-operation', 'Operation')}
|
||||
labelWidth={labelWidth}
|
||||
>
|
||||
<ExpressionTypeDropdown handleOnSelect={onSelectExpressionType}>
|
||||
<Button fill="outline" icon="angle-down" iconPlacement="right" variant="secondary">
|
||||
{expressionTypes.find(({ value }) => value === query.type)?.label}
|
||||
</Button>
|
||||
</ExpressionTypeDropdown>
|
||||
</InlineField>
|
||||
{helperText && <IconButton className={styles.infoIcon} name="info-circle" tooltip={helperText} />}
|
||||
</div>
|
||||
{renderExpressionType()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
operationRow: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
infoIcon: css({
|
||||
marginBottom: theme.spacing(0.5), // Align with the select field
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { ReactElement, useCallback, useMemo, memo } from 'react';
|
||||
|
||||
import { FeatureState, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { Dropdown, FeatureBadge, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { ExpressionQueryType, expressionTypes } from 'app/features/expressions/types';
|
||||
|
||||
const EXPRESSION_ICON_MAP = {
|
||||
[ExpressionQueryType.math]: 'calculator-alt',
|
||||
[ExpressionQueryType.reduce]: 'compress-arrows',
|
||||
[ExpressionQueryType.resample]: 'sync',
|
||||
[ExpressionQueryType.classic]: 'cog',
|
||||
[ExpressionQueryType.threshold]: 'sliders-v-alt',
|
||||
[ExpressionQueryType.sql]: 'database',
|
||||
} as const satisfies Record<ExpressionQueryType, string>;
|
||||
|
||||
interface ExpressionTypeDropdownProps {
|
||||
children: ReactElement;
|
||||
handleOnSelect: (value: ExpressionQueryType) => void;
|
||||
}
|
||||
|
||||
interface ExpressionMenuItemProps {
|
||||
item: SelectableValue<ExpressionQueryType>;
|
||||
onSelect: (value: ExpressionQueryType) => void;
|
||||
}
|
||||
|
||||
const ExpressionMenuItem = memo<ExpressionMenuItemProps>(({ item, onSelect }) => {
|
||||
const { value, label, description } = item;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const handleClick = useCallback(() => onSelect(value!), [value, onSelect]);
|
||||
|
||||
return (
|
||||
<Menu.Item
|
||||
component={() => (
|
||||
<div className={styles.expressionTypeItem} role="menuitem">
|
||||
<div className={styles.expressionTypeItemContent} data-testid={`expression-type-${value}`}>
|
||||
<Icon className={styles.icon} name={EXPRESSION_ICON_MAP[value!]} aria-hidden="true" />
|
||||
{label}
|
||||
{value === ExpressionQueryType.sql && <FeatureBadge featureState={FeatureState.new} />}
|
||||
</div>
|
||||
<Tooltip placement="right" content={description!}>
|
||||
<Icon className={styles.infoIcon} name="info-circle" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
key={value}
|
||||
label=""
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
ExpressionMenuItem.displayName = 'ExpressionMenuItem';
|
||||
|
||||
export const ExpressionTypeDropdown = memo<ExpressionTypeDropdownProps>(({ handleOnSelect, children }) => {
|
||||
const menuItems = useMemo(
|
||||
() => expressionTypes.map((item) => <ExpressionMenuItem key={item.value} item={item} onSelect={handleOnSelect} />),
|
||||
[handleOnSelect]
|
||||
);
|
||||
|
||||
const menuOverlay = useMemo(() => <Menu role="menu">{menuItems}</Menu>, [menuItems]);
|
||||
|
||||
return (
|
||||
<Dropdown placement="bottom-start" overlay={menuOverlay}>
|
||||
{children}
|
||||
</Dropdown>
|
||||
);
|
||||
});
|
||||
|
||||
ExpressionTypeDropdown.displayName = 'ExpressionTypeDropdown';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
expressionTypeItem: css({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
|
||||
expressionTypeItemContent: css({
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
|
||||
icon: css({
|
||||
color: theme.colors.text.secondary,
|
||||
flexShrink: 0,
|
||||
}),
|
||||
|
||||
infoIcon: css({
|
||||
opacity: 0.7,
|
||||
color: theme.colors.text.secondary,
|
||||
flexShrink: 0,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -30,12 +30,10 @@ interface Props {
|
||||
|
||||
export const SqlExpr = ({ onChange, refIds, query, alerting = false }: Props) => {
|
||||
const vars = useMemo(() => refIds.map((v) => v.value!), [refIds]);
|
||||
const initialQuery = `-- Run MySQL-dialect SQL against the tables returned from your data sources.
|
||||
-- Data source queries (ie "${vars[0]}") are available as tables and referenced by query-name
|
||||
-- Fields are available as columns, as returned from the data source.
|
||||
SELECT *
|
||||
FROM ${vars[0]}
|
||||
LIMIT 10`;
|
||||
const initialQuery = `SELECT *
|
||||
FROM ${vars[0]}
|
||||
LIMIT 10`;
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ height: 0 });
|
||||
|
||||
@@ -51,7 +51,7 @@ export const ConvertFieldTypeTransformerEditor = ({
|
||||
|
||||
const onSelectField = useCallback(
|
||||
(idx: number) => (value: string | undefined) => {
|
||||
const conversions = options.conversions;
|
||||
const conversions = [...options.conversions];
|
||||
conversions[idx] = { ...conversions[idx], targetField: value ?? '', dateFormat: undefined };
|
||||
onChange({
|
||||
...options,
|
||||
|
||||
+29
-29
@@ -36,55 +36,55 @@ describe('UrlAndAuthenticationSection', () => {
|
||||
expect(onOptionsChangeMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders DRBP warning for InfluxDB OSS 1.x and InfluxQL', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
jsonData: { product: 'InfluxDB OSS 1.x', version: InfluxVersion.InfluxQL },
|
||||
},
|
||||
};
|
||||
const productsRequiringDBRP = [
|
||||
'InfluxDB OSS 1.x',
|
||||
'InfluxDB OSS 2.x',
|
||||
'InfluxDB Enterprise 1.x',
|
||||
'InfluxDB Cloud (TSM)',
|
||||
'InfluxDB Cloud Serverless',
|
||||
];
|
||||
|
||||
render(<UrlAndAuthenticationSection {...props} />);
|
||||
expect(screen.getByText(/requires DRBP mapping/i)).toBeInTheDocument();
|
||||
describe('UrlAndAuthenticationSection', () => {
|
||||
it.each(productsRequiringDBRP)('renders DBRP warning for %s and InfluxQL', (product) => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
jsonData: {
|
||||
product,
|
||||
version: InfluxVersion.InfluxQL,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
render(<UrlAndAuthenticationSection {...props} />);
|
||||
expect(screen.getByText(/requires DBRP mapping/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders DRBP warning for InfluxDB OSS 2.x and InfluxQL', () => {
|
||||
it('does not render DBRP warning for SQL', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
jsonData: { product: 'InfluxDB OSS 2.x', version: InfluxVersion.InfluxQL },
|
||||
jsonData: { version: InfluxVersion.SQL },
|
||||
},
|
||||
};
|
||||
|
||||
render(<UrlAndAuthenticationSection {...props} />);
|
||||
expect(screen.getByText(/requires DRBP mapping/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/requires DBRP mapping/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render DRBP warning for InfluxDB OSS 1.x and Flux', () => {
|
||||
it('does not render DBRP warning for Flux', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
jsonData: { product: 'InfluxDB OSS 1.x', version: InfluxVersion.Flux },
|
||||
jsonData: { version: InfluxVersion.Flux },
|
||||
},
|
||||
};
|
||||
|
||||
render(<UrlAndAuthenticationSection {...props} />);
|
||||
expect(screen.queryByText(/requires DRBP mapping/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render DRBP warning for InfluxDB OSS 2.x and Flux', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
options: {
|
||||
...defaultProps.options,
|
||||
jsonData: { product: 'InfluxDB OSS 2.x', version: InfluxVersion.Flux },
|
||||
},
|
||||
};
|
||||
|
||||
render(<UrlAndAuthenticationSection {...props} />);
|
||||
expect(screen.queryByText(/requires DRBP mapping/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/requires DBRP mapping/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+10
-4
@@ -38,10 +38,16 @@ export const UrlAndAuthenticationSection = (props: Props) => {
|
||||
typeof v === 'string' && (v === InfluxVersion.Flux || v === InfluxVersion.InfluxQL || v === InfluxVersion.SQL);
|
||||
|
||||
// Database + Retention Policy (DBRP) mapping is required for InfluxDB OSS 1.x and 2.x when using InfluxQL
|
||||
const requiresDrbpMapping =
|
||||
const requiresDbrpMapping =
|
||||
options.jsonData.product &&
|
||||
options.jsonData.version === InfluxVersion.InfluxQL &&
|
||||
['InfluxDB OSS 1.x', 'InfluxDB OSS 2.x'].includes(options.jsonData.product);
|
||||
[
|
||||
'InfluxDB OSS 1.x',
|
||||
'InfluxDB OSS 2.x',
|
||||
'InfluxDB Enterprise 1.x',
|
||||
'InfluxDB Cloud (TSM)',
|
||||
'InfluxDB Cloud Serverless',
|
||||
].includes(options.jsonData.product);
|
||||
|
||||
const onProductChange = ({ value }: ComboboxOption) => {
|
||||
trackInfluxDBConfigV2ProductSelected({ product: value });
|
||||
@@ -113,8 +119,8 @@ export const UrlAndAuthenticationSection = (props: Props) => {
|
||||
|
||||
<Space v={2} />
|
||||
|
||||
{requiresDrbpMapping && (
|
||||
<Alert severity="warning" title="InfluxQL requires DRBP mapping">
|
||||
{requiresDbrpMapping && (
|
||||
<Alert severity="warning" title="InfluxQL requires DBRP mapping">
|
||||
InfluxDB OSS 1.x and 2.x users must configure a Database + Retention Policy (DBRP) mapping via the CLI or
|
||||
API before data can be queried.{' '}
|
||||
<TextLink href="https://docs.influxdata.com/influxdb/cloud/query-data/influxql/dbrp/" external>
|
||||
|
||||
@@ -30,8 +30,8 @@ export const CONFIG_SECTION_HEADERS_WITH_PDC = [
|
||||
];
|
||||
|
||||
export const HTTP_MODES: ComboboxOption[] = [
|
||||
{ label: 'GET', value: 'GET' },
|
||||
{ label: 'POST', value: 'POST' },
|
||||
{ label: 'GET', value: 'GET' },
|
||||
];
|
||||
|
||||
export const getInlineLabelStyles = (theme: GrafanaTheme2, transparent = false, width?: number | 'auto') => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/lezer-logql": "0.2.7",
|
||||
"@grafana/lezer-logql": "0.2.8",
|
||||
"@grafana/llm": "0.22.1",
|
||||
"@grafana/monaco-logql": "^0.0.8",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AutoCellOptionsEditor } from './cells/AutoCellOptionsEditor';
|
||||
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
|
||||
import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor';
|
||||
import { ImageCellOptionsEditor } from './cells/ImageCellOptionsEditor';
|
||||
import { PillCellOptionsEditor } from './cells/PillCellOptionsEditor';
|
||||
import { SparklineCellOptionsEditor } from './cells/SparklineCellOptionsEditor';
|
||||
|
||||
// The props that any cell type editor are expected
|
||||
@@ -77,6 +78,9 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
|
||||
{cellType === TableCellDisplayMode.Image && (
|
||||
<ImageCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
|
||||
)}
|
||||
{cellType === TableCellDisplayMode.Pill && (
|
||||
<PillCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -91,6 +95,7 @@ let cellDisplayModeOptions: Array<SelectableValue<TableCellOptions>> = [
|
||||
{ value: { type: TableCellDisplayMode.JSONView }, label: 'JSON View' },
|
||||
{ value: { type: TableCellDisplayMode.Image }, label: 'Image' },
|
||||
{ value: { type: TableCellDisplayMode.Actions }, label: 'Actions' },
|
||||
{ value: { type: TableCellDisplayMode.Pill }, label: 'Pill' },
|
||||
];
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ActionModel,
|
||||
@@ -57,6 +57,11 @@ export function TablePanel(props: Props) {
|
||||
|
||||
const enableSharedCrosshair = panelContext.sync && panelContext.sync() !== DashboardCursorSync.Off;
|
||||
|
||||
const _getActions = useCallback(
|
||||
(frame: DataFrame, field: Field, rowIndex: number) => getCellActions(frame, field, rowIndex, replaceVariables),
|
||||
[replaceVariables]
|
||||
);
|
||||
|
||||
const tableElement = (
|
||||
<TableNG
|
||||
height={tableHeight}
|
||||
@@ -75,8 +80,7 @@ export function TablePanel(props: Props) {
|
||||
timeRange={timeRange}
|
||||
enableSharedCrosshair={config.featureToggles.tableSharedCrosshair && enableSharedCrosshair}
|
||||
fieldConfig={fieldConfig}
|
||||
getActions={getCellActions}
|
||||
replaceVariables={replaceVariables}
|
||||
getActions={_getActions}
|
||||
structureRev={data.structureRev}
|
||||
/>
|
||||
);
|
||||
@@ -159,28 +163,39 @@ const getCellActions = (
|
||||
field: Field,
|
||||
rowIndex: number,
|
||||
replaceVariables: InterpolateFunction | undefined
|
||||
) => {
|
||||
const actions: Array<ActionModel<Field>> = [];
|
||||
const actionLookup = new Set<string>();
|
||||
): Array<ActionModel<Field>> => {
|
||||
const numActions = field.config.actions?.length ?? 0;
|
||||
|
||||
const actionsModel = getActions(
|
||||
dataFrame,
|
||||
field,
|
||||
field.state!.scopedVars!,
|
||||
replaceVariables ?? replaceVars,
|
||||
field.config.actions ?? [],
|
||||
{ valueRowIndex: rowIndex }
|
||||
);
|
||||
if (numActions > 0) {
|
||||
const actions = getActions(
|
||||
dataFrame,
|
||||
field,
|
||||
field.state!.scopedVars!,
|
||||
replaceVariables ?? replaceVars,
|
||||
field.config.actions ?? [],
|
||||
{ valueRowIndex: rowIndex }
|
||||
);
|
||||
|
||||
actionsModel.forEach((action) => {
|
||||
const key = `${action.title}`;
|
||||
if (!actionLookup.has(key)) {
|
||||
actions.push(action);
|
||||
actionLookup.add(key);
|
||||
if (actions.length === 1) {
|
||||
return actions;
|
||||
} else {
|
||||
const actionsOut: Array<ActionModel<Field>> = [];
|
||||
const actionLookup = new Set<string>();
|
||||
|
||||
actions.forEach((action) => {
|
||||
const key = action.title;
|
||||
|
||||
if (!actionLookup.has(key)) {
|
||||
actionsOut.push(action);
|
||||
actionLookup.add(key);
|
||||
}
|
||||
});
|
||||
|
||||
return actionsOut;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
return [];
|
||||
};
|
||||
|
||||
const tableStyles = {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { t } from '@grafana/i18n';
|
||||
import { TablePillCellOptions } from '@grafana/schema';
|
||||
import { Field, ColorPicker, RadioButtonGroup, Stack } from '@grafana/ui';
|
||||
|
||||
import { TableCellEditorProps } from '../TableCellOptionEditor';
|
||||
|
||||
const colorModeOptions: Array<{ value: 'auto' | 'fixed' | 'mapped'; label: string }> = [
|
||||
{ value: 'auto', label: 'Auto' },
|
||||
{ value: 'fixed', label: 'Fixed color' },
|
||||
{ value: 'mapped', label: 'Value mapping' },
|
||||
];
|
||||
|
||||
export const PillCellOptionsEditor = ({ cellOptions, onChange }: TableCellEditorProps<TablePillCellOptions>) => {
|
||||
const colorMode = cellOptions.colorMode || 'auto';
|
||||
|
||||
const onColorModeChange = (mode: 'auto' | 'fixed' | 'mapped') => {
|
||||
const updatedOptions = { ...cellOptions, colorMode: mode };
|
||||
onChange(updatedOptions);
|
||||
};
|
||||
|
||||
const onColorChange = (color: string) => {
|
||||
const updatedOptions = { ...cellOptions, color };
|
||||
onChange(updatedOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={1}>
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-color-mode', 'Color Mode')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-color-mode',
|
||||
'Choose how colors are assigned to pills'
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<RadioButtonGroup value={colorMode} onChange={onColorModeChange} options={colorModeOptions} />
|
||||
</Field>
|
||||
|
||||
{colorMode === 'fixed' && (
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-fixed-color', 'Fixed Color')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-fixed-color',
|
||||
'All pills in this column will use this color'
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<ColorPicker color={cellOptions.color || '#FF780A'} onChange={onColorChange} enableNamedColors={false} />
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{colorMode === 'mapped' && (
|
||||
<Field
|
||||
label={t('table.pill-cell-options-editor.label-value-mappings-info', 'Value Mappings')}
|
||||
description={t(
|
||||
'table.pill-cell-options-editor.description-value-mappings-info',
|
||||
'For Value Mappings either use the global table Value Mappings or the Field overrides Value Mappings. The default will fall back to the Color Scheme. '
|
||||
)}
|
||||
noMargin
|
||||
>
|
||||
<div> </div>
|
||||
</Field>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -4435,7 +4435,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Veřejná",
|
||||
"tooltip-view-as-scene": "Zobrazit jako scénu"
|
||||
}
|
||||
},
|
||||
@@ -5211,7 +5210,6 @@
|
||||
"playlist-next": "Přejít na další nástěnku",
|
||||
"playlist-previous": "Přejít na předchozí nástěnku",
|
||||
"playlist-stop": "Zastavit playlist",
|
||||
"public-dashboard": "Veřejná",
|
||||
"refresh": "Obnovit nástěnku",
|
||||
"save": "Uložit nástěnku",
|
||||
"save-dashboard": {
|
||||
@@ -5512,15 +5510,9 @@
|
||||
"type": "Typ"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Hodnota ještě není uložena v databázi Grafany",
|
||||
"not-yet-saved": "Hodnota není uložena v databázi Grafany",
|
||||
"open-pull-request-in-git-hub": "Otevřít pull request v GitHubu",
|
||||
"title-dashboard-loaded-branch-git-hub": "Tato nástěnka je načtena z větve na GitHubu.",
|
||||
"title-dashboard-loaded-external-repository": "Tato nástěnka je načtena z externího úložiště",
|
||||
"title-dashboard-loaded-request-git-hub": "Tato nástěnka je načtena z pull request na GitHubu.",
|
||||
"title-error-loading-dashboard": "Chyba při načítání nástěnky",
|
||||
"value-not-saved": "Hodnota ještě není uložena v databázi Grafany",
|
||||
"view-pull-request-in-git-hub": "Zobrazit pull request v GitHubu"
|
||||
"title-error-loading-dashboard": "Chyba při načítání nástěnky"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7259,6 +7251,7 @@
|
||||
"when": "KDY"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Činnost"
|
||||
},
|
||||
"math": {
|
||||
@@ -8551,7 +8544,7 @@
|
||||
"usage-count_other": "Použito na {{count}} nástěnkách"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Hledat podle názvu nebo popisu"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Načítání panelu knihovny…"
|
||||
@@ -8696,12 +8689,18 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_few": "",
|
||||
"indexed-label_many": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_few": "",
|
||||
"parsedl-label_many": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_few": "",
|
||||
"structured-metadata_many": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8758,6 +8757,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8771,9 +8771,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10369,6 +10371,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Tato funkce je v současné době v aktivním vývoji. Pro nejlepší zážitek a nejnovější vylepšení doporučujeme použít <2>noční sestavení</2> Grafany."
|
||||
@@ -11834,6 +11847,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Öffentlich",
|
||||
"tooltip-view-as-scene": "Als Szene anzeigen"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Zum nächsten Dashboard",
|
||||
"playlist-previous": "Zum vorherigen Dashboard",
|
||||
"playlist-stop": "Wiedergabeliste stoppen",
|
||||
"public-dashboard": "Öffentlich",
|
||||
"refresh": "Dashboard aktualisieren",
|
||||
"save": "Dashboard speichern",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Typ"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Der Wert ist noch nicht in der Grafana-Datenbank gespeichert",
|
||||
"not-yet-saved": "Der Wert wird nicht in der Grafana-Datenbank gespeichert",
|
||||
"open-pull-request-in-git-hub": "Pull-Anfrage in GitHub öffnen",
|
||||
"title-dashboard-loaded-branch-git-hub": "Dieses Dashboard wird von einem Branch in GitHub geladen.",
|
||||
"title-dashboard-loaded-external-repository": "Dieses Dashboard wird von einem externen Repository geladen",
|
||||
"title-dashboard-loaded-request-git-hub": "Dieses Dashboard wird von einer Pull-Anfrage in GitHub geladen.",
|
||||
"title-error-loading-dashboard": "Fehler beim Laden des Dashboards",
|
||||
"value-not-saved": "Der Wert ist noch nicht in der Grafana-Datenbank gespeichert",
|
||||
"view-pull-request-in-git-hub": "Pull-Anfrage in GitHub anzeigen"
|
||||
"title-error-loading-dashboard": "Fehler beim Laden des Dashboards"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "WANN"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Operation"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Verwendet bei {{count}} Dashboards"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Suche nach Name oder Beschreibung"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Bibliotheks-Panel wird geladen ..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Diese Funktion befindet sich momentan in der aktiven Entwicklung. Für die bestmögliche Nutzererfahrung und die neuesten Verbesserungen empfehlen wir die Nutzung von <2>Nightly Build</2> von Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -7209,6 +7209,7 @@
|
||||
"when": "WHEN"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "Run MySQL-dialect SQL against the tables returned from your data sources. Data source queries (ie \"A\", \"B\") are available as tables and referenced by query-name. Fields are available as columns, as returned from the data source.",
|
||||
"label-operation": "Operation"
|
||||
},
|
||||
"math": {
|
||||
@@ -11768,6 +11769,14 @@
|
||||
"name-show-table-footer": "Show table footer",
|
||||
"name-show-table-header": "Show table header",
|
||||
"name-wrap-header-text": "Wrap header text",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "Choose how colors are assigned to pills",
|
||||
"description-fixed-color": "All pills in this column will use this color",
|
||||
"description-value-mappings-info": "For Value Mappings either use the global table Value Mappings or the Field overrides Value Mappings. The default will fall back to the Color Scheme. ",
|
||||
"label-color-mode": "Color Mode",
|
||||
"label-fixed-color": "Fixed Color",
|
||||
"label-value-mappings-info": "Value Mappings"
|
||||
},
|
||||
"placeholder-column-width": "auto",
|
||||
"placeholder-fields": "All Numeric Fields"
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Pública",
|
||||
"tooltip-view-as-scene": "Ver como escena"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Ir al siguiente panel de control",
|
||||
"playlist-previous": "Ir al panel de control anterior",
|
||||
"playlist-stop": "Detener la lista de reproducción",
|
||||
"public-dashboard": "Pública",
|
||||
"refresh": "Actualizar panel de control",
|
||||
"save": "Guardar panel de control",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Tipo"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "El valor aún no se ha guardado en la base de datos de Grafana",
|
||||
"not-yet-saved": "El valor no se ha guardado en la base de datos de Grafana",
|
||||
"open-pull-request-in-git-hub": "Abrir solicitud de extracción en GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Este panel de control se carga desde una rama en GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Este panel de control se carga desde un repositorio externo",
|
||||
"title-dashboard-loaded-request-git-hub": "Este panel de control se carga desde una solicitud de extracción en GitHub.",
|
||||
"title-error-loading-dashboard": "Error al cargar el panel de control",
|
||||
"value-not-saved": "El valor aún no se ha guardado en la base de datos de Grafana",
|
||||
"view-pull-request-in-git-hub": "Ver solicitud de extracción en GitHub"
|
||||
"title-error-loading-dashboard": "Error al cargar el panel de control"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "CUÁNDO"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Operación"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Utilizado en {{count}} dashboards"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Buscar por nombre o descripción"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Cargando panel de la librería..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Esta función se encuentra actualmente en desarrollo activo. Para obtener la mejor experiencia y las últimas mejoras, recomendamos utilizar la <2>compilación nocturna</2> de Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Public",
|
||||
"tooltip-view-as-scene": "Afficher en tant que scène"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Accéder au tableau de bord suivant",
|
||||
"playlist-previous": "Accéder au tableau de bord précédent",
|
||||
"playlist-stop": "Arrêter la liste de lecture",
|
||||
"public-dashboard": "Public",
|
||||
"refresh": "Actualiser le tableau de bord",
|
||||
"save": "Enregistrer le tableau de bord",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Type"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "La valeur n'est pas encore enregistrée dans la base de données Grafana",
|
||||
"not-yet-saved": "La valeur n'est pas enregistrée dans la base de données Grafana",
|
||||
"open-pull-request-in-git-hub": "Ouvrir la demande de fusion dans GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Ce tableau de bord est chargé à partir d'une branche dans GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Ce tableau de bord est chargé à partir d'un référentiel externe",
|
||||
"title-dashboard-loaded-request-git-hub": "Ce tableau de bord est chargé à partir d'une demande de fusion dans GitHub.",
|
||||
"title-error-loading-dashboard": "Erreur lors du chargement du tableau de bord",
|
||||
"value-not-saved": "La valeur n'est pas encore enregistrée dans la base de données Grafana",
|
||||
"view-pull-request-in-git-hub": "Afficher la demande de fusion dans GitHub"
|
||||
"title-error-loading-dashboard": "Erreur lors du chargement du tableau de bord"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "QUAND"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Opération"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Utilisé sur {{count}} tableaux de bord"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Rechercher par nom ou par description"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Chargement du panneau de la bibliothèque..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Cette fonctionnalité est actuellement en cours de développement. Pour une expérience optimale et les dernières améliorations, nous vous recommandons d’utiliser la <2>compilation nocturne</2> de Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Nyilvános",
|
||||
"tooltip-view-as-scene": "Megtekintés jelenetként"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Ugrás a következő irányítópulthoz",
|
||||
"playlist-previous": "Ugrás az előző irányítópulthoz",
|
||||
"playlist-stop": "Lejátszási lista leállítása",
|
||||
"public-dashboard": "Nyilvános",
|
||||
"refresh": "Irányítópult frissítése",
|
||||
"save": "Irányítópult mentése",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Típus"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Az érték még nincs mentve a Grafana-adatbázisban",
|
||||
"not-yet-saved": "Az érték nincs mentve a Grafana-adatbázisban",
|
||||
"open-pull-request-in-git-hub": "Összefésülési kérelem megnyitása a GitHubon",
|
||||
"title-dashboard-loaded-branch-git-hub": "Ez az irányítópult a GitHub egyik ágából töltődik be.",
|
||||
"title-dashboard-loaded-external-repository": "Ez az irányítópult külső adattárból töltődik be",
|
||||
"title-dashboard-loaded-request-git-hub": "Ez az irányítópult a GitHub egyik összefésülési kérelméből töltődik be.",
|
||||
"title-error-loading-dashboard": "Hiba történt az irányítópult betöltésekor",
|
||||
"value-not-saved": "Az érték még nincs mentve a Grafana-adatbázisban",
|
||||
"view-pull-request-in-git-hub": "Összefésülési kérelem megtekintése a GitHubon"
|
||||
"title-error-loading-dashboard": "Hiba történt az irányítópult betöltésekor"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "WHEN"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Művelet"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "{{count}} irányítópulton használatos"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Keresés név vagy leírás alapján"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Könyvtárpanel betöltése…"
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Ez a funkció jelenleg aktív fejlesztés alatt áll. A legjobb élmény és a legújabb fejlesztések érdekében javasoljuk a Grafana <2>éjszakai buildjének</2> használatát."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4381,7 +4381,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Publik",
|
||||
"tooltip-view-as-scene": "Tampilkan sebagai Scene"
|
||||
}
|
||||
},
|
||||
@@ -5154,7 +5153,6 @@
|
||||
"playlist-next": "Buka dasbor berikutnya",
|
||||
"playlist-previous": "Buka dasbor sebelumnya",
|
||||
"playlist-stop": "Hentikan daftar putar",
|
||||
"public-dashboard": "Publik",
|
||||
"refresh": "Muat ulang dasbor",
|
||||
"save": "Simpan dasbor",
|
||||
"save-dashboard": {
|
||||
@@ -5455,15 +5453,9 @@
|
||||
"type": "Jenis"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Nilai belum disimpan dalam database Grafana",
|
||||
"not-yet-saved": "Nilai tidak disimpan dalam database Grafana",
|
||||
"open-pull-request-in-git-hub": "Buka permintaan penggabungan di GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Dasbor ini dimuat dari cabang di GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Dasbor ini dimuat dari repositori eksternal",
|
||||
"title-dashboard-loaded-request-git-hub": "Dasbor ini dimuat dari permintaan penggabungan di GitHub.",
|
||||
"title-error-loading-dashboard": "Kesalahan saat memuat dasbor",
|
||||
"value-not-saved": "Nilai belum disimpan dalam database Grafana",
|
||||
"view-pull-request-in-git-hub": "Lihat permintaan penggabungan di GitHub"
|
||||
"title-error-loading-dashboard": "Kesalahan saat memuat dasbor"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7196,6 +7188,7 @@
|
||||
"when": "KAPAN"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Pengoperasian"
|
||||
},
|
||||
"math": {
|
||||
@@ -8479,7 +8472,7 @@
|
||||
"usage-count_other": "Digunakan pada {{count}} dasbor"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Cari berdasarkan nama atau deskripsi"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Memuat panel pustaka..."
|
||||
@@ -8618,12 +8611,9 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8680,6 +8670,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8693,9 +8684,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10282,6 +10275,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Fitur ini saat ini sedang dalam pengembangan aktif. Untuk pengalaman terbaik dan peningkatan terbaru, kami merekomendasikan Anda untuk menggunakan <2>versi nightly </2> Grafana."
|
||||
@@ -11726,6 +11730,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Pubblico",
|
||||
"tooltip-view-as-scene": "Visualizza come Scena"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Vai al dashboard successivo",
|
||||
"playlist-previous": "Vai al dashboard precedente",
|
||||
"playlist-stop": "Interrompi playlist",
|
||||
"public-dashboard": "Pubblico",
|
||||
"refresh": "Aggiorna dashboard",
|
||||
"save": "Salva dashboard",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Tipo"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Il valore non è ancora stato salvato nel database di Grafana",
|
||||
"not-yet-saved": "Il valore non è stato salvato nel database di Grafana",
|
||||
"open-pull-request-in-git-hub": "Apri richiesta pull in GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Questo dashboard viene caricato da un ramo in GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Questo dashboard viene caricato da un repository esterno",
|
||||
"title-dashboard-loaded-request-git-hub": "Questo dashboard viene caricato da una richiesta pull in GitHub.",
|
||||
"title-error-loading-dashboard": "Errore durante il caricamento del dashboard",
|
||||
"value-not-saved": "Il valore non è ancora stato salvato nel database di Grafana",
|
||||
"view-pull-request-in-git-hub": "Visualizza richiesta pull in GitHub"
|
||||
"title-error-loading-dashboard": "Errore durante il caricamento del dashboard"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "QUANDO"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Operazione"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Utilizzato su {{count}} dashboard"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Cerca per nome o descrizione"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Caricamento del pannello della libreria in corso..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Questa funzione è attualmente in fase di sviluppo attivo. Per un'esperienza ottimale e gli ultimi miglioramenti, consigliamo di utilizzare la <2>nightly build</2> di Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4381,7 +4381,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "パブリック",
|
||||
"tooltip-view-as-scene": "シーンとして表示"
|
||||
}
|
||||
},
|
||||
@@ -5154,7 +5153,6 @@
|
||||
"playlist-next": "次のダッシュボードに移動",
|
||||
"playlist-previous": "前のダッシュボードに戻る",
|
||||
"playlist-stop": "プレイリストを停止",
|
||||
"public-dashboard": "パブリック",
|
||||
"refresh": "ダッシュボードを更新",
|
||||
"save": "ダッシュボードを保存",
|
||||
"save-dashboard": {
|
||||
@@ -5455,15 +5453,9 @@
|
||||
"type": "タイプ"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "値はまだGrafanaデータベースに保存されていません",
|
||||
"not-yet-saved": "値はGrafanaデータベースに保存されていません",
|
||||
"open-pull-request-in-git-hub": "GitHubでプルリクエストを開く",
|
||||
"title-dashboard-loaded-branch-git-hub": "このダッシュボードはGitHubのブランチから読み込まれます。",
|
||||
"title-dashboard-loaded-external-repository": "このダッシュボードは外部リポジトリから読み込まれます",
|
||||
"title-dashboard-loaded-request-git-hub": "このダッシュボードは、GitHubのプルリクエストから読み込まれます。",
|
||||
"title-error-loading-dashboard": "ダッシュボードの読み込み中にエラーが発生しました",
|
||||
"value-not-saved": "値はまだGrafanaデータベースに保存されていません",
|
||||
"view-pull-request-in-git-hub": "GitHubでプルリクエストを表示する"
|
||||
"title-error-loading-dashboard": "ダッシュボードの読み込み中にエラーが発生しました"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7196,6 +7188,7 @@
|
||||
"when": "条件"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "操作"
|
||||
},
|
||||
"math": {
|
||||
@@ -8479,7 +8472,7 @@
|
||||
"usage-count_other": "{{count}}件のダッシュボードで使用中"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "名前と詳細で検索"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "ライブラリパネル読み込み中..."
|
||||
@@ -8618,12 +8611,9 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8680,6 +8670,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8693,9 +8684,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10282,6 +10275,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "この機能は現在開発中です。最高の体験と最新の改善を利用するには、Grafanaの<2>ナイトリービルド</2>の使用をお勧めします。"
|
||||
@@ -11726,6 +11730,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4381,7 +4381,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "공개",
|
||||
"tooltip-view-as-scene": "씬으로 보기"
|
||||
}
|
||||
},
|
||||
@@ -5154,7 +5153,6 @@
|
||||
"playlist-next": "다음 대시보드로 이동",
|
||||
"playlist-previous": "이전 대시보드로 이동",
|
||||
"playlist-stop": "플레이리스트 중지",
|
||||
"public-dashboard": "공개",
|
||||
"refresh": "대시보드 새로 고침",
|
||||
"save": "대시보드 저장",
|
||||
"save-dashboard": {
|
||||
@@ -5455,15 +5453,9 @@
|
||||
"type": "유형"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "값이 아직 Grafana 데이터베이스에 저장되지 않았습니다.",
|
||||
"not-yet-saved": "값이 Grafana 데이터베이스에 저장되지 않았습니다.",
|
||||
"open-pull-request-in-git-hub": "GitHub에서 풀 요청 열기",
|
||||
"title-dashboard-loaded-branch-git-hub": "이 대시보드는 GitHub의 브랜치에서 로딩됩니다.",
|
||||
"title-dashboard-loaded-external-repository": "이 대시보드는 외부 리포지토리에서 로딩됩니다.",
|
||||
"title-dashboard-loaded-request-git-hub": "이 대시보드는 GitHub의 풀 요청에서 로딩됩니다.",
|
||||
"title-error-loading-dashboard": "대시보드 로딩 중 오류 발생",
|
||||
"value-not-saved": "값이 아직 Grafana 데이터베이스에 저장되지 않았습니다.",
|
||||
"view-pull-request-in-git-hub": "GitHub에서 풀 요청 보기"
|
||||
"title-error-loading-dashboard": "대시보드 로딩 중 오류 발생"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7196,6 +7188,7 @@
|
||||
"when": "다음과 같은 때"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "연산"
|
||||
},
|
||||
"math": {
|
||||
@@ -8479,7 +8472,7 @@
|
||||
"usage-count_other": "{{count}}개의 대시보드에서 사용됨"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "이름 또는 설명으로 검색"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "라이브러리 패널 로딩 중..."
|
||||
@@ -8618,12 +8611,9 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8680,6 +8670,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8693,9 +8684,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10282,6 +10275,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "이 기능은 현재 적극적으로 개발 중입니다. 최상의 경험과 최신 개선 사항 적용을 위해 Grafana의 <2>야간 빌드</2>를 사용하는 것이 좋습니다."
|
||||
@@ -11726,6 +11730,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Openbaar",
|
||||
"tooltip-view-as-scene": "Weergeven als scène"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Naar het volgende dashboard",
|
||||
"playlist-previous": "Naar het vorige dashboard",
|
||||
"playlist-stop": "Afspeellijst stoppen",
|
||||
"public-dashboard": "Openbaar",
|
||||
"refresh": "Dashboard vernieuwen",
|
||||
"save": "Dashboard opslaan",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Type"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "De waarde is nog niet opgeslagen in de Grafana-database",
|
||||
"not-yet-saved": "De waarde is niet opgeslagen in de Grafana-database",
|
||||
"open-pull-request-in-git-hub": "Pull-verzoek openen in GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Dit dashboard wordt geladen vanuit een filiaal in GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Dit dashboard wordt geladen vanuit een externe repository",
|
||||
"title-dashboard-loaded-request-git-hub": "Dit dashboard wordt geladen vanuit een pull-verzoek in GitHub.",
|
||||
"title-error-loading-dashboard": "Er is een fout opgetreden bij het laden van het dashboard",
|
||||
"value-not-saved": "De waarde is nog niet opgeslagen in de Grafana-database",
|
||||
"view-pull-request-in-git-hub": "Pull-verzoek bekijken in GitHub"
|
||||
"title-error-loading-dashboard": "Er is een fout opgetreden bij het laden van het dashboard"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "WANNEER"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Werking"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Gebruikt op {{count}} dashboards"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Zoeken op naam of beschrijving"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Bibliotheekpaneel laden..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Er wordt momenteel actief gewerkt aan de ontwikkeling van deze functie. Voor de beste ervaring en de nieuwste verbeteringen raden we je aan om de <2>nachtelijke versie</2> van Grafana te gebruiken."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4435,7 +4435,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Publiczny",
|
||||
"tooltip-view-as-scene": "Wyświetl jako scenę"
|
||||
}
|
||||
},
|
||||
@@ -5211,7 +5210,6 @@
|
||||
"playlist-next": "Przejdź do następnego pulpitu",
|
||||
"playlist-previous": "Przejdź do poprzedniego pulpitu",
|
||||
"playlist-stop": "Zatrzymaj autoodtwarzanie",
|
||||
"public-dashboard": "Publiczny",
|
||||
"refresh": "Odśwież pulpit",
|
||||
"save": "Zapisz pulpit",
|
||||
"save-dashboard": {
|
||||
@@ -5512,15 +5510,9 @@
|
||||
"type": "Typ"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Wartość nie została jeszcze zapisana w bazie danych Grafana",
|
||||
"not-yet-saved": "Wartość nie została zapisana w bazie danych Grafana",
|
||||
"open-pull-request-in-git-hub": "Otwórz pull request w GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Ten pulpit nawigacyjny jest ładowany z gałęzi w GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Ten pulpit nawigacyjny jest ładowany z zewnętrznego repozytorium",
|
||||
"title-dashboard-loaded-request-git-hub": "Ten pulpit nawigacyjny jest ładowany z pull request w GitHub.",
|
||||
"title-error-loading-dashboard": "Błąd wczytywania pulpitu",
|
||||
"value-not-saved": "Wartość nie została jeszcze zapisana w bazie danych Grafana",
|
||||
"view-pull-request-in-git-hub": "Wyświetl pull request w GitHub"
|
||||
"title-error-loading-dashboard": "Błąd wczytywania pulpitu"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7259,6 +7251,7 @@
|
||||
"when": "KIEDY"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Operacja"
|
||||
},
|
||||
"math": {
|
||||
@@ -8551,7 +8544,7 @@
|
||||
"usage-count_other": "Używane na {{count}} pulpitu"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Szukaj według nazwy lub opisu"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Wczytywanie panelu biblioteki…"
|
||||
@@ -8696,12 +8689,18 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_few": "",
|
||||
"indexed-label_many": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_few": "",
|
||||
"parsedl-label_many": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_few": "",
|
||||
"structured-metadata_many": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8758,6 +8757,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8771,9 +8771,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10369,6 +10371,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Ta funkcja jest obecnie aktywnie rozwijana. Aby uzyskać najlepsze wrażenia i najnowsze ulepszenia, zalecamy korzystanie z <2>nocnej kompilacji</2> aplikacji Grafana."
|
||||
@@ -11834,6 +11847,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Pública",
|
||||
"tooltip-view-as-scene": "Visualizar como cena"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Ir para o próximo painel de controle",
|
||||
"playlist-previous": "Ir para o painel de controle anterior",
|
||||
"playlist-stop": "Parar lista de reprodução",
|
||||
"public-dashboard": "Público",
|
||||
"refresh": "Atualizar painel de controle",
|
||||
"save": "Salvar painel de controle",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Tipo"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "O valor ainda não está salvo no banco de dados da Grafana",
|
||||
"not-yet-saved": "O valor não está salvo no banco de dados da Grafana",
|
||||
"open-pull-request-in-git-hub": "Abrir solicitação de pull no GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Este painel de controle é carregado a partir de um branch no GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Este painel de controle é carregado a partir de um repositório externo",
|
||||
"title-dashboard-loaded-request-git-hub": "Este painel de controle é carregado a partir de uma solicitação de pull no GitHub.",
|
||||
"title-error-loading-dashboard": "Erro ao carregar o painel de controle",
|
||||
"value-not-saved": "O valor ainda não está salvo no banco de dados da Grafana",
|
||||
"view-pull-request-in-git-hub": "Ver solicitação de pull no GitHub"
|
||||
"title-error-loading-dashboard": "Erro ao carregar o painel de controle"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "QUANDO"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Operação"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Usado em {{count}} painéis"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Pesquisar por nome ou descrição"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Carregando painel da biblioteca…"
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Este recurso está em desenvolvimento ativo no momento. Para ter uma melhor experiência e conferir as melhorias mais recentes, recomendamos usar a <2>compilação noturna</2> da Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Público",
|
||||
"tooltip-view-as-scene": "Ver como cena"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Ir para o próximo painel de controlo",
|
||||
"playlist-previous": "Ir para o painel de controlo anterior",
|
||||
"playlist-stop": "Parar a lista de reprodução",
|
||||
"public-dashboard": "Público",
|
||||
"refresh": "Atualizar o painel de controlo",
|
||||
"save": "Guardar o painel de controlo",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Tipo"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "O valor ainda não está guardado na base de dados da Grafana",
|
||||
"not-yet-saved": "O valor não está guardado na base de dados da Grafana",
|
||||
"open-pull-request-in-git-hub": "Abrir pedido pull no GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Este painel de controlo é carregado a partir de um ramo no GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Este painel de controlo é carregado a partir de um repositório externo",
|
||||
"title-dashboard-loaded-request-git-hub": "Este painel de controlo é carregado a partir de um pedido pull no GitHub.",
|
||||
"title-error-loading-dashboard": "Erro ao carregar o painel de controlo",
|
||||
"value-not-saved": "O valor ainda não está guardado na base de dados da Grafana",
|
||||
"view-pull-request-in-git-hub": "Visualizar pedido pull no GitHub"
|
||||
"title-error-loading-dashboard": "Erro ao carregar o painel de controlo"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "QUANDO"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Funcionamento"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Utilizado em {{count}} painéis de controlo"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Pesquisar por nome ou descrição"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "A carregar painel de biblioteca..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Esta funcionalidade está atualmente em desenvolvimento ativo. Para a melhor experiência e as melhorias mais recentes, recomendamos a utilização da <2>compilação noturna</2> da Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4435,7 +4435,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Общедоступный",
|
||||
"tooltip-view-as-scene": "Просмотр в виде сцены"
|
||||
}
|
||||
},
|
||||
@@ -5211,7 +5210,6 @@
|
||||
"playlist-next": "Перейти к следующему дашборду",
|
||||
"playlist-previous": "Перейти к предыдущему дашборду",
|
||||
"playlist-stop": "Остановить плейлист",
|
||||
"public-dashboard": "Общедоступный",
|
||||
"refresh": "Обновить дашборд",
|
||||
"save": "Сохранить дашборд",
|
||||
"save-dashboard": {
|
||||
@@ -5512,15 +5510,9 @@
|
||||
"type": "Тип"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Значение еще не сохранено в базе данных Grafana",
|
||||
"not-yet-saved": "Значение не сохранено в базе данных Grafana",
|
||||
"open-pull-request-in-git-hub": "Открыть запрос на включение изменений в GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Дашборд загружается из ветви в GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Дашборд загружается из внешнего репозитория",
|
||||
"title-dashboard-loaded-request-git-hub": "Дашборд загружается из запроса на включение изменений в GitHub.",
|
||||
"title-error-loading-dashboard": "Ошибка при загрузке дашборда",
|
||||
"value-not-saved": "Значение еще не сохранено в базе данных Grafana",
|
||||
"view-pull-request-in-git-hub": "Просмотр запроса на включение изменений в GitHub"
|
||||
"title-error-loading-dashboard": "Ошибка при загрузке дашборда"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7259,6 +7251,7 @@
|
||||
"when": "КОГДА"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Операция"
|
||||
},
|
||||
"math": {
|
||||
@@ -8551,7 +8544,7 @@
|
||||
"usage-count_other": "Используется на {{count}} дашборда"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Поиск по названию или описанию"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Загрузка панели библиотеки..."
|
||||
@@ -8696,12 +8689,18 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_few": "",
|
||||
"indexed-label_many": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_few": "",
|
||||
"parsedl-label_many": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_few": "",
|
||||
"structured-metadata_many": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8758,6 +8757,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8771,9 +8771,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10369,6 +10371,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "В настоящее время эта функция находится в стадии активной разработки. Чтобы обеспечить максимальное удобство пользования и получить доступ к последним улучшениям, рекомендуем использовать <2>ночную сборку</2> Grafana."
|
||||
@@ -11834,6 +11847,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Offentlig",
|
||||
"tooltip-view-as-scene": "Visa som scen"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Gå till nästa instrumentpanel",
|
||||
"playlist-previous": "Gå till föregående instrumentpanel",
|
||||
"playlist-stop": "Stoppa spellista",
|
||||
"public-dashboard": "Offentlig",
|
||||
"refresh": "Uppdatera instrumentpanel",
|
||||
"save": "Spara instrumentpanel",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Typ"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Värdet har ännu inte sparats i Grafana-databasen",
|
||||
"not-yet-saved": "Värdet sparades inte i Grafana-databasen",
|
||||
"open-pull-request-in-git-hub": "Öppna pull-begäran i GitHub",
|
||||
"title-dashboard-loaded-branch-git-hub": "Den här instrumentpanelen laddas från en gren i GitHub.",
|
||||
"title-dashboard-loaded-external-repository": "Den här instrumentpanelen laddas från ett externt arkiv",
|
||||
"title-dashboard-loaded-request-git-hub": "Den här instrumentpanelen laddas från en pull-begäran i GitHub.",
|
||||
"title-error-loading-dashboard": "Fel vid laddning av instrumentpanel",
|
||||
"value-not-saved": "Värdet har ännu inte sparats i Grafana-databasen",
|
||||
"view-pull-request-in-git-hub": "Visa pull-begäran i GitHub"
|
||||
"title-error-loading-dashboard": "Fel vid laddning av instrumentpanel"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "NÄR"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Drift"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "Används på {{count}} instrumentpaneler"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Sök efter namn eller beskrivning"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Laddar bibliotekspanel …"
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Den här funktionen är för närvarande under aktiv utveckling. För den bästa upplevelsen och de senaste förbättringarna rekommenderar vi att du använder <2> nightly-versionen</2> av Grafana."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4399,7 +4399,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "Herkese açık",
|
||||
"tooltip-view-as-scene": "Sahne olarak görüntüle"
|
||||
}
|
||||
},
|
||||
@@ -5173,7 +5172,6 @@
|
||||
"playlist-next": "Sonraki panoya git",
|
||||
"playlist-previous": "Önceki panoya git",
|
||||
"playlist-stop": "Oynatma listesini durdur",
|
||||
"public-dashboard": "Herkese açık",
|
||||
"refresh": "Panoyu yenile",
|
||||
"save": "Panoyu kaydet",
|
||||
"save-dashboard": {
|
||||
@@ -5474,15 +5472,9 @@
|
||||
"type": "Tür"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "Değer henüz Grafana veri tabanına kaydedilmedi",
|
||||
"not-yet-saved": "Değer Grafana veri tabanına kaydedilmedi",
|
||||
"open-pull-request-in-git-hub": "GitHub'da çekme isteği aç",
|
||||
"title-dashboard-loaded-branch-git-hub": "Bu pano, GitHub'daki bir daldan yüklendi.",
|
||||
"title-dashboard-loaded-external-repository": "Bu pano, haricî bir depodan yüklendi.",
|
||||
"title-dashboard-loaded-request-git-hub": "Bu pano, GitHub'daki bir çekme isteğinden yüklendi.",
|
||||
"title-error-loading-dashboard": "Pano yüklenirken hata oluştu",
|
||||
"value-not-saved": "Değer henüz Grafana veri tabanına kaydedilmedi",
|
||||
"view-pull-request-in-git-hub": "GitHub'da çekme isteğini görüntüle"
|
||||
"title-error-loading-dashboard": "Pano yüklenirken hata oluştu"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7217,6 +7209,7 @@
|
||||
"when": "NE ZAMAN"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "Çalıştırma"
|
||||
},
|
||||
"math": {
|
||||
@@ -8503,7 +8496,7 @@
|
||||
"usage-count_other": "{{count}} panoda kullanılıyor"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "Ada veya açıklamaya göre ara"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "Kütüphane paneli yükleniyor..."
|
||||
@@ -8644,12 +8637,12 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_one": "",
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_one": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_one": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8706,6 +8699,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8719,9 +8713,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10311,6 +10307,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "Bu özellik şu anda aktif geliştirme aşamasındadır. En iyi deneyim ve en yeni geliştirmeler için Grafana'nın <2>gecelik derleme</2> sürümünü kullanmanızı öneririz."
|
||||
@@ -11762,6 +11769,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4381,7 +4381,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "公开",
|
||||
"tooltip-view-as-scene": "作为场景查看"
|
||||
}
|
||||
},
|
||||
@@ -5154,7 +5153,6 @@
|
||||
"playlist-next": "前往下一个仪表板",
|
||||
"playlist-previous": "前往上一个仪表板",
|
||||
"playlist-stop": "停止播放列表",
|
||||
"public-dashboard": "公开",
|
||||
"refresh": "刷新仪表板",
|
||||
"save": "保存仪表板",
|
||||
"save-dashboard": {
|
||||
@@ -5455,15 +5453,9 @@
|
||||
"type": "类型"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "该值尚未保存在 Grafana 数据库中",
|
||||
"not-yet-saved": "该值未保存在 Grafana 数据库中",
|
||||
"open-pull-request-in-git-hub": "在 GitHub 中打开拉取请求",
|
||||
"title-dashboard-loaded-branch-git-hub": "此数据面板从 GitHub 的分支加载。",
|
||||
"title-dashboard-loaded-external-repository": "此数据面板从外部存储库加载",
|
||||
"title-dashboard-loaded-request-git-hub": "此数据面板从 GitHub 的拉取请求加载。",
|
||||
"title-error-loading-dashboard": "加载数据面板时出错",
|
||||
"value-not-saved": "该值尚未保存在 Grafana 数据库中",
|
||||
"view-pull-request-in-git-hub": "在 GitHub 中查看拉取请求"
|
||||
"title-error-loading-dashboard": "加载数据面板时出错"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7196,6 +7188,7 @@
|
||||
"when": "满足以下条件时"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "运算"
|
||||
},
|
||||
"math": {
|
||||
@@ -8479,7 +8472,7 @@
|
||||
"usage-count_other": "用于 {{count}} 个数据面板"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "按名称或描述搜索"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "正在加载库面板..."
|
||||
@@ -8618,12 +8611,9 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8680,6 +8670,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8693,9 +8684,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10282,6 +10275,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "此功能目前正在积极开发中。为了获得最佳体验和最新改进,我们建议您使用 Grafana 的<2>夜间版本</2>。"
|
||||
@@ -11726,6 +11730,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -4381,7 +4381,6 @@
|
||||
}
|
||||
},
|
||||
"render-left-actions": {
|
||||
"text-public": "公共",
|
||||
"tooltip-view-as-scene": "以場景形式檢視"
|
||||
}
|
||||
},
|
||||
@@ -5154,7 +5153,6 @@
|
||||
"playlist-next": "前往下一個儀表板",
|
||||
"playlist-previous": "前往上一個儀表板",
|
||||
"playlist-stop": "停止播放清單",
|
||||
"public-dashboard": "公共",
|
||||
"refresh": "重新整理儀表板",
|
||||
"save": "儲存儀表板",
|
||||
"save-dashboard": {
|
||||
@@ -5455,15 +5453,9 @@
|
||||
"type": "類型"
|
||||
},
|
||||
"dashboard-preview-banner": {
|
||||
"not-saved": "該數值尚未儲存在 Grafana 資料庫中",
|
||||
"not-yet-saved": "該數值未儲存在 Grafana 資料庫中",
|
||||
"open-pull-request-in-git-hub": "在 GitHub 中開啟拉取請求",
|
||||
"title-dashboard-loaded-branch-git-hub": "此儀表板是從 GitHub 的分支載入。",
|
||||
"title-dashboard-loaded-external-repository": "此儀表板是從外部存放庫載入",
|
||||
"title-dashboard-loaded-request-git-hub": "此儀表板是從 GitHub 的拉取請求載入。",
|
||||
"title-error-loading-dashboard": "載入控制面板發生錯誤",
|
||||
"value-not-saved": "該數值尚未儲存在 Grafana 資料庫中",
|
||||
"view-pull-request-in-git-hub": "在 GitHub 中檢視拉取請求"
|
||||
"title-error-loading-dashboard": "載入控制面板發生錯誤"
|
||||
},
|
||||
"dashboard-scene": {
|
||||
"text": {
|
||||
@@ -7196,6 +7188,7 @@
|
||||
"when": "當"
|
||||
},
|
||||
"expression-query-editor": {
|
||||
"helper-text-sql": "",
|
||||
"label-operation": "操作"
|
||||
},
|
||||
"math": {
|
||||
@@ -8479,7 +8472,7 @@
|
||||
"usage-count_other": "用於 {{count}} 個儀表板"
|
||||
},
|
||||
"library-panels-search": {
|
||||
"placeholder-search-by-name-or-description": "按名稱或描述搜尋"
|
||||
"placeholder-search-by-name-or-description": ""
|
||||
},
|
||||
"loading-indicator": {
|
||||
"loading-library-panel": "正在載入資料庫面板…"
|
||||
@@ -8618,12 +8611,9 @@
|
||||
"fields": {
|
||||
"type": {
|
||||
"loki": {
|
||||
"indexed-label": "",
|
||||
"indexed-label-plural": "",
|
||||
"parsed-label-plural": "",
|
||||
"parsedl-label": "",
|
||||
"structured-metadata": "",
|
||||
"structured-metadata-plural": ""
|
||||
"indexed-label_other": "",
|
||||
"parsedl-label_other": "",
|
||||
"structured-metadata_other": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8680,6 +8670,7 @@
|
||||
"close": "",
|
||||
"copy-shortlink": "",
|
||||
"copy-to-clipboard": "",
|
||||
"displayed-fields-section": "",
|
||||
"fields": {
|
||||
"adhoc-statistics": "",
|
||||
"copy-value-to-clipboard": "",
|
||||
@@ -8693,9 +8684,11 @@
|
||||
"fields-section": "",
|
||||
"hide-log-line": "",
|
||||
"links-section": "",
|
||||
"log-line-field": "",
|
||||
"log-line-section": "",
|
||||
"no-details": "",
|
||||
"pin-line": "",
|
||||
"remove-displayed-field": "",
|
||||
"search": {
|
||||
"no-results": ""
|
||||
},
|
||||
@@ -10282,6 +10275,17 @@
|
||||
"label-workflow": ""
|
||||
}
|
||||
},
|
||||
"provisioned-resource-preview-banner": {
|
||||
"preview-banner": {
|
||||
"not-saved": "",
|
||||
"open-pull-request-in-git-hub": "",
|
||||
"view-pull-request-in-git-hub": ""
|
||||
},
|
||||
"title-dashboard-loaded-branch-git-hub": "",
|
||||
"title-dashboard-loaded-pull-request-git-hub": "",
|
||||
"title-folder-created-branch-git-hub": "",
|
||||
"title-folder-created-pull-request-git-hub": ""
|
||||
},
|
||||
"provisioning": {
|
||||
"banner": {
|
||||
"message": "此功能目前正在積極開發中。為了獲得最佳體驗和最新改進版本,建議使用 Grafana 的<2>夜間版本</2>。"
|
||||
@@ -11726,6 +11730,14 @@
|
||||
"name-show-table-footer": "",
|
||||
"name-show-table-header": "",
|
||||
"name-wrap-header-text": "",
|
||||
"pill-cell-options-editor": {
|
||||
"description-color-mode": "",
|
||||
"description-fixed-color": "",
|
||||
"description-value-mappings-info": "",
|
||||
"label-color-mode": "",
|
||||
"label-fixed-color": "",
|
||||
"label-value-mappings-info": ""
|
||||
},
|
||||
"placeholder-column-width": "",
|
||||
"placeholder-fields": ""
|
||||
},
|
||||
|
||||
@@ -2720,7 +2720,7 @@ __metadata:
|
||||
"@emotion/css": "npm:11.13.5"
|
||||
"@grafana/data": "npm:12.1.0-pre"
|
||||
"@grafana/e2e-selectors": "npm:12.1.0-pre"
|
||||
"@grafana/lezer-logql": "npm:0.2.7"
|
||||
"@grafana/lezer-logql": "npm:0.2.8"
|
||||
"@grafana/llm": "npm:0.22.1"
|
||||
"@grafana/monaco-logql": "npm:^0.0.8"
|
||||
"@grafana/plugin-configs": "npm:12.1.0-pre"
|
||||
@@ -3283,12 +3283,12 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/lezer-logql@npm:0.2.7":
|
||||
version: 0.2.7
|
||||
resolution: "@grafana/lezer-logql@npm:0.2.7"
|
||||
"@grafana/lezer-logql@npm:0.2.8":
|
||||
version: 0.2.8
|
||||
resolution: "@grafana/lezer-logql@npm:0.2.8"
|
||||
peerDependencies:
|
||||
"@lezer/lr": ^1.0.0
|
||||
checksum: 10/606a9dc77b3b3751e1f325d6b1a8994b1bafef7fe0f6f3980ee7d184244b373f828960f46a748746e765615352ed8928d10f22ff06cede588fcc9d32e70a68d8
|
||||
checksum: 10/56b31f9479037201b07d27602c023a2c2373758f232b02a4e5c501dbfabe6f708ddd2a4f8d059b5aad576297137bc2836ed3f520eceee6ffcd3001ad53c73f5c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3714,7 +3714,7 @@ __metadata:
|
||||
"@testing-library/jest-dom": "npm:6.6.3"
|
||||
"@testing-library/react": "npm:16.2.0"
|
||||
"@testing-library/user-event": "npm:14.6.1"
|
||||
"@types/chance": "npm:1.1.6"
|
||||
"@types/chance": "npm:^1.1.7"
|
||||
"@types/common-tags": "npm:^1.8.0"
|
||||
"@types/d3": "npm:7.4.3"
|
||||
"@types/hoist-non-react-statics": "npm:3.3.6"
|
||||
@@ -3738,7 +3738,7 @@ __metadata:
|
||||
"@types/tinycolor2": "npm:1.4.6"
|
||||
"@types/uuid": "npm:10.0.0"
|
||||
calculate-size: "npm:1.1.1"
|
||||
chance: "npm:1.1.12"
|
||||
chance: "npm:^1.1.13"
|
||||
classnames: "npm:2.5.1"
|
||||
common-tags: "npm:1.8.2"
|
||||
core-js: "npm:3.40.0"
|
||||
@@ -8902,10 +8902,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/chance@npm:1.1.6, @types/chance@npm:^1.1.3":
|
||||
version: 1.1.6
|
||||
resolution: "@types/chance@npm:1.1.6"
|
||||
checksum: 10/f4366f1b3144d143af3e6f0fad2ed1db7b9bdfa7d82d40944e9619d57fe7e6b60e8c1452f47a8ededa6b2188932879518628ecd9aac81c40384ded39c26338ba
|
||||
"@types/chance@npm:^1.1.7":
|
||||
version: 1.1.7
|
||||
resolution: "@types/chance@npm:1.1.7"
|
||||
checksum: 10/5b3bf4ef0b7a2f6554f7767d7f081b4f613b45e74e74450d4da9c10cd12162420fc6ff5bb0abea143d6f7c43b62776b7a0e75d242cbe25c729fe020a1735e16f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -11726,21 +11726,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"autoprefixer@npm:10.4.20":
|
||||
version: 10.4.20
|
||||
resolution: "autoprefixer@npm:10.4.20"
|
||||
"autoprefixer@npm:10.4.21":
|
||||
version: 10.4.21
|
||||
resolution: "autoprefixer@npm:10.4.21"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
caniuse-lite: "npm:^1.0.30001646"
|
||||
browserslist: "npm:^4.24.4"
|
||||
caniuse-lite: "npm:^1.0.30001702"
|
||||
fraction.js: "npm:^4.3.7"
|
||||
normalize-range: "npm:^0.1.2"
|
||||
picocolors: "npm:^1.0.1"
|
||||
picocolors: "npm:^1.1.1"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
bin:
|
||||
autoprefixer: bin/autoprefixer
|
||||
checksum: 10/d3c4b562fc4af2393623a0207cc336f5b9f94c4264ae1c316376904c279702ce2b12dc3f27205f491195d1e29bb52ffc269970ceb0f271f035fadee128a273f7
|
||||
checksum: 10/5d7aeee78ef362a6838e12312908516a8ac5364414175273e5cff83bbff67612755b93d567f3aa01ce318342df48aeab4b291847b5800c780e58c458f61a98a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12287,17 +12287,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0, browserslist@npm:^4.24.3":
|
||||
version: 4.25.0
|
||||
resolution: "browserslist@npm:4.25.0"
|
||||
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.24.0, browserslist@npm:^4.24.3, browserslist@npm:^4.24.4, browserslist@npm:^4.24.5":
|
||||
version: 4.25.1
|
||||
resolution: "browserslist@npm:4.25.1"
|
||||
dependencies:
|
||||
caniuse-lite: "npm:^1.0.30001718"
|
||||
electron-to-chromium: "npm:^1.5.160"
|
||||
caniuse-lite: "npm:^1.0.30001726"
|
||||
electron-to-chromium: "npm:^1.5.173"
|
||||
node-releases: "npm:^2.0.19"
|
||||
update-browserslist-db: "npm:^1.1.3"
|
||||
bin:
|
||||
browserslist: cli.js
|
||||
checksum: 10/4a5442b1a0d09c4c64454f184b8fed17d8c3e202034bf39de28f74497d7bd28dddee121b2bab4e34825fe0ed4c166d84e32a39f576c76fce73c1f8f05e4b6ee6
|
||||
checksum: 10/bfb5511b425886279bbe2ea44d10e340c8aea85866c9d45083c13491d049b6362e254018c0afbf56d41ceeb64f994957ea8ae98dbba74ef1e54ef901c8732987
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12567,10 +12567,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001718":
|
||||
version: 1.0.30001723
|
||||
resolution: "caniuse-lite@npm:1.0.30001723"
|
||||
checksum: 10/edab89e84a2b257cf640f0bac1f25f92c699ade86143b2affc73403468f894023416a9f4a99e5345c933956990b005a2facfb87ac4517c8ccb588819bb62453b
|
||||
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001726":
|
||||
version: 1.0.30001727
|
||||
resolution: "caniuse-lite@npm:1.0.30001727"
|
||||
checksum: 10/6155a4141332c337d6317325bea58a09036a65f45bd9bd834ec38978b40c27d214baa04d25b21a5661664f3fbd00cb830e2bdb7eee8df09970bdd98a71f4dabf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12677,10 +12677,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chance@npm:1.1.12, chance@npm:^1.0.10":
|
||||
version: 1.1.12
|
||||
resolution: "chance@npm:1.1.12"
|
||||
checksum: 10/8700d5a66e27b47f4bdcf68f48489e1a490f39cd8bc8e39cac67c089b3f8d04b4bbc6710db0e85e802ad698cf3440d92784861208d2c248820355404b85b3f30
|
||||
"chance@npm:^1.1.13":
|
||||
version: 1.1.13
|
||||
resolution: "chance@npm:1.1.13"
|
||||
checksum: 10/968e31ce9b8ce554c8a84fb66d85f6d06a33517bf21355ab760e347a0aaaece0652a23c791406826d4efe619b1aa4e7b66fb42acbd07859ba36ec2498a167b8c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12849,15 +12849,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chrome-remote-interface@npm:0.33.2":
|
||||
version: 0.33.2
|
||||
resolution: "chrome-remote-interface@npm:0.33.2"
|
||||
"chrome-remote-interface@npm:0.33.3":
|
||||
version: 0.33.3
|
||||
resolution: "chrome-remote-interface@npm:0.33.3"
|
||||
dependencies:
|
||||
commander: "npm:2.11.x"
|
||||
ws: "npm:^7.2.0"
|
||||
bin:
|
||||
chrome-remote-interface: bin/client.js
|
||||
checksum: 10/fa82c76c5af629f5fbccb22c383604650f8679571385b8610783fde6f006b899c48e1522be551fd9670de59c1a2d93beb152a55651f24ba9c91ca0f8df3f581c
|
||||
checksum: 10/65d07afc8f97fad6326bd94f0c4ef004d76b16841015c72d09aefbfa4df62b47407067a01c886535a706c36b23070b40fc3edb96444665438efa1d4f65cc3db8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13933,14 +13933,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"css-minimizer-webpack-plugin@npm:7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "css-minimizer-webpack-plugin@npm:7.0.0"
|
||||
"css-minimizer-webpack-plugin@npm:7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "css-minimizer-webpack-plugin@npm:7.0.2"
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.25"
|
||||
cssnano: "npm:^7.0.1"
|
||||
cssnano: "npm:^7.0.4"
|
||||
jest-worker: "npm:^29.7.0"
|
||||
postcss: "npm:^8.4.38"
|
||||
postcss: "npm:^8.4.40"
|
||||
schema-utils: "npm:^4.2.0"
|
||||
serialize-javascript: "npm:^6.0.2"
|
||||
peerDependencies:
|
||||
@@ -13958,7 +13958,7 @@ __metadata:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
checksum: 10/47d8f8a38c97496759f1676b5344231d48bfb205cc272e163a113d4cd40daddd17f8eb719d1f1071cf3ec4d62fd83537801cba598d7c653657aa97f1461e4a8b
|
||||
checksum: 10/80ada5f059900a3b474e33ff0f10c0e5f5a37b7525fa7ef30812b6049bcbc28fbda1d703d8f76724a97449114306de5cd7a7af15003a16784e6c9cc374dfedc8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -14098,64 +14098,64 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cssnano-preset-default@npm:^7.0.6":
|
||||
version: 7.0.6
|
||||
resolution: "cssnano-preset-default@npm:7.0.6"
|
||||
"cssnano-preset-default@npm:^7.0.7":
|
||||
version: 7.0.7
|
||||
resolution: "cssnano-preset-default@npm:7.0.7"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
browserslist: "npm:^4.24.5"
|
||||
css-declaration-sorter: "npm:^7.2.0"
|
||||
cssnano-utils: "npm:^5.0.0"
|
||||
postcss-calc: "npm:^10.0.2"
|
||||
postcss-colormin: "npm:^7.0.2"
|
||||
postcss-convert-values: "npm:^7.0.4"
|
||||
postcss-discard-comments: "npm:^7.0.3"
|
||||
postcss-discard-duplicates: "npm:^7.0.1"
|
||||
postcss-discard-empty: "npm:^7.0.0"
|
||||
postcss-discard-overridden: "npm:^7.0.0"
|
||||
postcss-merge-longhand: "npm:^7.0.4"
|
||||
postcss-merge-rules: "npm:^7.0.4"
|
||||
postcss-minify-font-values: "npm:^7.0.0"
|
||||
postcss-minify-gradients: "npm:^7.0.0"
|
||||
postcss-minify-params: "npm:^7.0.2"
|
||||
postcss-minify-selectors: "npm:^7.0.4"
|
||||
postcss-normalize-charset: "npm:^7.0.0"
|
||||
postcss-normalize-display-values: "npm:^7.0.0"
|
||||
postcss-normalize-positions: "npm:^7.0.0"
|
||||
postcss-normalize-repeat-style: "npm:^7.0.0"
|
||||
postcss-normalize-string: "npm:^7.0.0"
|
||||
postcss-normalize-timing-functions: "npm:^7.0.0"
|
||||
postcss-normalize-unicode: "npm:^7.0.2"
|
||||
postcss-normalize-url: "npm:^7.0.0"
|
||||
postcss-normalize-whitespace: "npm:^7.0.0"
|
||||
postcss-ordered-values: "npm:^7.0.1"
|
||||
postcss-reduce-initial: "npm:^7.0.2"
|
||||
postcss-reduce-transforms: "npm:^7.0.0"
|
||||
postcss-svgo: "npm:^7.0.1"
|
||||
postcss-unique-selectors: "npm:^7.0.3"
|
||||
cssnano-utils: "npm:^5.0.1"
|
||||
postcss-calc: "npm:^10.1.1"
|
||||
postcss-colormin: "npm:^7.0.3"
|
||||
postcss-convert-values: "npm:^7.0.5"
|
||||
postcss-discard-comments: "npm:^7.0.4"
|
||||
postcss-discard-duplicates: "npm:^7.0.2"
|
||||
postcss-discard-empty: "npm:^7.0.1"
|
||||
postcss-discard-overridden: "npm:^7.0.1"
|
||||
postcss-merge-longhand: "npm:^7.0.5"
|
||||
postcss-merge-rules: "npm:^7.0.5"
|
||||
postcss-minify-font-values: "npm:^7.0.1"
|
||||
postcss-minify-gradients: "npm:^7.0.1"
|
||||
postcss-minify-params: "npm:^7.0.3"
|
||||
postcss-minify-selectors: "npm:^7.0.5"
|
||||
postcss-normalize-charset: "npm:^7.0.1"
|
||||
postcss-normalize-display-values: "npm:^7.0.1"
|
||||
postcss-normalize-positions: "npm:^7.0.1"
|
||||
postcss-normalize-repeat-style: "npm:^7.0.1"
|
||||
postcss-normalize-string: "npm:^7.0.1"
|
||||
postcss-normalize-timing-functions: "npm:^7.0.1"
|
||||
postcss-normalize-unicode: "npm:^7.0.3"
|
||||
postcss-normalize-url: "npm:^7.0.1"
|
||||
postcss-normalize-whitespace: "npm:^7.0.1"
|
||||
postcss-ordered-values: "npm:^7.0.2"
|
||||
postcss-reduce-initial: "npm:^7.0.3"
|
||||
postcss-reduce-transforms: "npm:^7.0.1"
|
||||
postcss-svgo: "npm:^7.0.2"
|
||||
postcss-unique-selectors: "npm:^7.0.4"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/686e7652d01ad4337dbad17b22fdb9cf132cf4664fddd05194da13a1f44f1177697745bbc6da73083941356280e89fe2cceacb6f422cc4522d70ff51db83cd63
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/1ca9b739531acc2dff66347cc1b6195da0549058b5b00b9d36c3f241535ad67476218f61201cfb15e8b460357ec42414aa53bf78a7f01ee26ac26b7852e6c244
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cssnano-utils@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "cssnano-utils@npm:5.0.0"
|
||||
"cssnano-utils@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "cssnano-utils@npm:5.0.1"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/89ed5b8ca554697b4ae285e0d3e134fccc9a0471adda57c8fba17a2bace2f062b9fcf7aeaf66fbd7fabddca8a15a6b1e5ccb70a2783421ae1ac164f779d9f24e
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/cdf37315d3cf9726e10ce842b18e148e4df1d1d18d292540e724d5a96994901abc631c8894328c39ab70c864449a8a83f8fc117114fdcbade204e5e65898af90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cssnano@npm:^7.0.1":
|
||||
version: 7.0.6
|
||||
resolution: "cssnano@npm:7.0.6"
|
||||
"cssnano@npm:^7.0.4":
|
||||
version: 7.0.7
|
||||
resolution: "cssnano@npm:7.0.7"
|
||||
dependencies:
|
||||
cssnano-preset-default: "npm:^7.0.6"
|
||||
lilconfig: "npm:^3.1.2"
|
||||
cssnano-preset-default: "npm:^7.0.7"
|
||||
lilconfig: "npm:^3.1.3"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/12b1e1f2b52ff2ba0ecb470e51f8fb3298d976bf91a51c7d2854793ea1e2af5d3c40385a85ad82d2117c84b9528d08f4bfecbb14949c6014d953dae34260952b
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/c5b3123757834537f818e0f3eb6b20da51a194fefed599632f7ddd600c9e25d38abe38a22582a579660a49368a146c294e2096b2837cbeeda51ddfc85b108601
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15384,10 +15384,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.5.160":
|
||||
version: 1.5.167
|
||||
resolution: "electron-to-chromium@npm:1.5.167"
|
||||
checksum: 10/078093a38e7295e575f381943f62914f49b53dd73506af2ce3e59332835c42b487ad02ff1207dfdcb33a5886d74a98e352c04431c0537366d9999a79c7d15c94
|
||||
"electron-to-chromium@npm:^1.5.173":
|
||||
version: 1.5.180
|
||||
resolution: "electron-to-chromium@npm:1.5.180"
|
||||
checksum: 10/8d7f68650427f6bcb107ee1dcbe18f68b5c582601653095bec653fa898bd8427be4b1836581ce74405dca7cb36ebfc265a85c186e750b4de6e6f3ac4cfeef71b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -18122,7 +18122,7 @@ __metadata:
|
||||
"@grafana/flamegraph": "workspace:*"
|
||||
"@grafana/google-sdk": "npm:0.3.2"
|
||||
"@grafana/i18n": "workspace:*"
|
||||
"@grafana/lezer-logql": "npm:0.2.7"
|
||||
"@grafana/lezer-logql": "npm:0.2.8"
|
||||
"@grafana/llm": "npm:0.22.1"
|
||||
"@grafana/monaco-logql": "npm:^0.0.8"
|
||||
"@grafana/o11y-ds-frontend": "workspace:*"
|
||||
@@ -18178,7 +18178,7 @@ __metadata:
|
||||
"@testing-library/user-event": "npm:14.6.1"
|
||||
"@types/babel__core": "npm:^7"
|
||||
"@types/babel__preset-env": "npm:^7"
|
||||
"@types/chance": "npm:^1.1.3"
|
||||
"@types/chance": "npm:^1.1.7"
|
||||
"@types/common-tags": "npm:^1.8.0"
|
||||
"@types/confusing-browser-globals": "npm:^1"
|
||||
"@types/d3": "npm:7.4.3"
|
||||
@@ -18238,15 +18238,15 @@ __metadata:
|
||||
"@visx/tooltip": "npm:3.12.0"
|
||||
"@welldone-software/why-did-you-render": "npm:8.0.3"
|
||||
ansicolor: "npm:2.0.3"
|
||||
autoprefixer: "npm:10.4.20"
|
||||
autoprefixer: "npm:10.4.21"
|
||||
babel-loader: "npm:9.2.1"
|
||||
baron: "npm:3.0.3"
|
||||
blob-polyfill: "npm:9.0.20240710"
|
||||
brace: "npm:0.11.1"
|
||||
browserslist: "npm:^4.21.4"
|
||||
centrifuge: "npm:5.3.5"
|
||||
chance: "npm:^1.0.10"
|
||||
chrome-remote-interface: "npm:0.33.2"
|
||||
chance: "npm:^1.1.13"
|
||||
chrome-remote-interface: "npm:0.33.3"
|
||||
classnames: "npm:2.5.1"
|
||||
codeowners: "npm:^5.1.1"
|
||||
combokeys: "npm:^3.0.0"
|
||||
@@ -18258,7 +18258,7 @@ __metadata:
|
||||
crashme: "npm:0.0.15"
|
||||
croner: "npm:^9.0.0"
|
||||
css-loader: "npm:7.1.2"
|
||||
css-minimizer-webpack-plugin: "npm:7.0.0"
|
||||
css-minimizer-webpack-plugin: "npm:7.0.2"
|
||||
cypress: "npm:14.3.2"
|
||||
cypress-file-upload: "npm:5.0.8"
|
||||
cypress-recurse: "npm:^1.35.3"
|
||||
@@ -21787,7 +21787,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lilconfig@npm:^3.1.2, lilconfig@npm:^3.1.3":
|
||||
"lilconfig@npm:^3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "lilconfig@npm:3.1.3"
|
||||
checksum: 10/b932ce1af94985f0efbe8896e57b1f814a48c8dbd7fc0ef8469785c6303ed29d0090af3ccad7e36b626bfca3a4dc56cc262697e9a8dd867623cf09a39d54e4c3
|
||||
@@ -23295,12 +23295,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.8":
|
||||
version: 3.3.8
|
||||
resolution: "nanoid@npm:3.3.8"
|
||||
"nanoid@npm:^3.3.11, nanoid@npm:^3.3.8":
|
||||
version: 3.3.11
|
||||
resolution: "nanoid@npm:3.3.11"
|
||||
bin:
|
||||
nanoid: bin/nanoid.cjs
|
||||
checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40
|
||||
checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25105,7 +25105,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.1":
|
||||
"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "picocolors@npm:1.1.1"
|
||||
checksum: 10/e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045
|
||||
@@ -25303,79 +25303,79 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-calc@npm:^10.0.2":
|
||||
version: 10.0.2
|
||||
resolution: "postcss-calc@npm:10.0.2"
|
||||
"postcss-calc@npm:^10.1.1":
|
||||
version: 10.1.1
|
||||
resolution: "postcss-calc@npm:10.1.1"
|
||||
dependencies:
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
postcss-selector-parser: "npm:^7.0.0"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.38
|
||||
checksum: 10/12d497e632b4a12f7d33507ed6f74db2dd01f9b9cc1f9986271af16b118d25f959dc255777a91d742e0431f400a90b8540d00533fc0513f34c1840a491cf2bee
|
||||
checksum: 10/16a25ec594cfbbda439fd2939820f78ed4e7b8b5ab458aed7283b05fffabe68e1d4e1f4821fac798095f10539371676cd690bd27927adefab1911ff69b33d62c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-colormin@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-colormin@npm:7.0.2"
|
||||
"postcss-colormin@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-colormin@npm:7.0.3"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
browserslist: "npm:^4.24.5"
|
||||
caniuse-api: "npm:^3.0.0"
|
||||
colord: "npm:^2.9.3"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/cb83d95d21668c770e5268f50ec6f8cd5d991d65123bafd3aa4a697580609c62d0078e704c4b7820db57638bf386084b253885b1e86263f580e8a393a687e973
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/b9016d205eaf61a25efb187264a2ce35cb59aa1734b946268abcd747b5796e0d855c081b460ead4042a17c6806e011b57ee543b9e1f6312620f8daf661a7e40c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-convert-values@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-convert-values@npm:7.0.4"
|
||||
"postcss-convert-values@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "postcss-convert-values@npm:7.0.5"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
browserslist: "npm:^4.24.5"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/077481cc98514965acf335cdacae4f604be86f4153ed3bcfdd2c4c54058182d0b472f859931d55d9aeb01600f08fff6a88a21539adbb6169019fda8b22f064ef
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/67920f9ba823a6f6aa3b46c3a098c2d4a7a2a32349971cfa6ce986e08e7cbae6badeb23de680d36d1439e7d3f2cdbf26f5ee080a66f2823931c1d3f8146bc2a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-discard-comments@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-discard-comments@npm:7.0.3"
|
||||
"postcss-discard-comments@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-discard-comments@npm:7.0.4"
|
||||
dependencies:
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/f7c994df0d2de75d876f0db7ebd5b63718ef7ee5336a35f5f753f8ea115ecf8be26d5d2ad8800e833f18b33da6e018af82de7b5f0aa69e6338d3e0aff46348d4
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/a09ac248bfbd6f2baa72b84873a876f4113df0fb5e9dd10808f6bbb310473fcd7905cc4639dbfd3ad8a5444053d42f7bb644a6934e95305820bdedc731d3c80a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-discard-duplicates@npm:^7.0.1":
|
||||
"postcss-discard-duplicates@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-discard-duplicates@npm:7.0.2"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/2da841b5c0117528e56e1ccda28924339c03fdb93dab61b767cebb9a9e4a2a077498d00e0c97c9ec36a534f98d6f358e6236f30913c184f90d51f6d302f4f0f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-discard-empty@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-discard-duplicates@npm:7.0.1"
|
||||
resolution: "postcss-discard-empty@npm:7.0.1"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/0c757bb542caf017740157a2e29186ae83085bb42cd8e5ea3649fa039cc3d505ccaca739b1aed6c89e1f0a7f18440f77c3f49e4b99f45efd767c863d6647af94
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/39977000657e78202da891ae6300593e40e1c8a756f1d9707087390e47a410739c394c35e902130556efb5808e6701b3b34b89facf7a9e56533d617dd9597049
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-discard-empty@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-discard-empty@npm:7.0.0"
|
||||
"postcss-discard-overridden@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-discard-overridden@npm:7.0.1"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/0c5cea198057727765855dbb43b5f16bd4d7da8c783fea8d18ad445ad3457681a7bc1696fda6bf16313e6fadaf86d519470aff68f02378b8b413e60023b70d57
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-discard-overridden@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-discard-overridden@npm:7.0.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/e41c448305f96a93ec97a4a8ce2932a123283898041ff38ed2f7a35fcb76d937f448c2c8efb7d74d53d38b4ebf9163ae12935297bb99baec2f6751776b0ea29b
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/a0e67314b696591396e6bb371cdd57537e06f63e9fa0d742fe678decf600bed0cdcfa481487bce91b3732bdd7c46338f9102ccc8180c41032811e99962883715
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25406,78 +25406,78 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-merge-longhand@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-merge-longhand@npm:7.0.4"
|
||||
"postcss-merge-longhand@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "postcss-merge-longhand@npm:7.0.5"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
stylehacks: "npm:^7.0.4"
|
||||
stylehacks: "npm:^7.0.5"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/b94b98a9b21bc8671aa0fba96491e8e2deea57c9bbfe9a74305400a36035b63764d8bfbdc6bda047887b665b92b91361a8bbc1cb9c14f80b3792feef19881005
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/3378fc3a196082dfdb9acff94efbfa0de95ed86bf87f485285e775fd3c21218e5a243e363ad80b96237edb454776f7c1deea28c37afb8b96ddfaf5cfe8bd606b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-merge-rules@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-merge-rules@npm:7.0.4"
|
||||
"postcss-merge-rules@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "postcss-merge-rules@npm:7.0.5"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
browserslist: "npm:^4.24.5"
|
||||
caniuse-api: "npm:^3.0.0"
|
||||
cssnano-utils: "npm:^5.0.0"
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
cssnano-utils: "npm:^5.0.1"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/f67a4f6e814c5e7ce990a3c3d699e1c1dba7e79c5cb3a11795534d47b0fa257d27465e248546b45104d8278dfcbd07d9fbceb8046fcdfac86fe6340ca3c85f9a
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/fa490791ea5e907e4498701593252ce33df468a821e5f3acf5f126f73c8262189c13ca7a0c1645ae3d66a46a03cf930048e10d808182a3e9bec78af30a02893a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-minify-font-values@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-minify-font-values@npm:7.0.0"
|
||||
"postcss-minify-font-values@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-minify-font-values@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/8578c1d1d4d65ca34db5ac0cccc7b73500040e52a3abb8abc7e5b6e47e5f72c88bfe5f3b19847556a2a68082245009d693a7c098b8bc58e7f9640abba4e80194
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/6578a1fd293e202e738ce38d91d71c08ba970f4a998edff48022cb21ec23ef26bf7d284ddb41d6e51bf20b5b5676fe142de1bd092a76d2ef982d5ee1d6b00190
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-minify-gradients@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-minify-gradients@npm:7.0.0"
|
||||
"postcss-minify-gradients@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-minify-gradients@npm:7.0.1"
|
||||
dependencies:
|
||||
colord: "npm:^2.9.3"
|
||||
cssnano-utils: "npm:^5.0.0"
|
||||
cssnano-utils: "npm:^5.0.1"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/9649e255ad954e67e0d7c2111b0f1681a93e8cba7179a547491eacf135d64596dfee9774b589d7a46ee3ace673a026113e56e734d6ab19297367f11dd3104c0e
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/4aa782331c5d1826e549b3940eefb54e2d51f5c5a2c5f5537384bfe6eac45bfe7ba4535c03cd1642d8a27ab088f56c3682b55f5dd2c3f7969b715692e0c1102b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-minify-params@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-minify-params@npm:7.0.2"
|
||||
"postcss-minify-params@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-minify-params@npm:7.0.3"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
cssnano-utils: "npm:^5.0.0"
|
||||
browserslist: "npm:^4.24.5"
|
||||
cssnano-utils: "npm:^5.0.1"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/26b6ce4db3cdefcceb7a00b64dfbd27dee4194b55708937dddd5c4000c1f02013dc0659e62e799dc1ce1f1a697961cec55a2a746a4f59d54ccae4b68adf41768
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/97de22d6ba0310685d33b530dbfeefa930f7ac48effe623fc8a4a59d2b98bed221d0d2edad4f2e1f4590322240d0e1e94bdb162069c40b5d7ae00c58637c90c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-minify-selectors@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-minify-selectors@npm:7.0.4"
|
||||
"postcss-minify-selectors@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "postcss-minify-selectors@npm:7.0.5"
|
||||
dependencies:
|
||||
cssesc: "npm:^3.0.0"
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/54c74dcb098819417e95ec2b5ecdd33a2c6fdccea2346e110037c762d37644e11f83d67e6b0c93405f2b7cc28880ca0a07ad4d6618330436f7b8b84d719b85fb
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/12580d9a17c146c9e9bb604b4887085d897554317590cee91e0f28e2a4757c18e09299365a44eae25e848e65d53b845928dfa56a9d0199d0e159d525732fbf89
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25525,136 +25525,136 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-charset@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-charset@npm:7.0.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/a41043fb81a1d5b3b05e8b317de7fe123854a4535f9ce2904a16196a32b3565d2fd6ac59a9842e337cf1bb298dcc108cbdbc6a5d4a500aec3520d759e951a8de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-display-values@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-display-values@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/55bbfb4dac3bf9bcc2aed30057c0bc968927b5337b372ee2dd825d6ec626c18d1481b0e8dd928d4cab70c3e8a2e6708d6115b14bebd34fe4462eb15aacff35f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-positions@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-positions@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/a6b982e567ddf1ad4120aaf898056f2fdbe5f6cae1d475fef22cb1f025c9bfe37df5511a4353b9f13d01feae8b1d9638c1deb70537058312262647052d004f64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-repeat-style@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-repeat-style@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/f8ef8cf5ac6232f1d0615a97f21ea464a6930484b58421c87e0f9e626b1bb52916592f25e4f9874f424b1529807b170d8805d45878aa8293ea0608dd753230c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-string@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-string@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/23ea7dd7b28880dfafd0880ab782d65186ab94a4cf789b8723f9666020c7f7c8b97546e0dc46d08da3f71a873bb6db41cd69a4cafb4fde4a85f97ef83ee38bae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-timing-functions@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-timing-functions@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/f85870b3c8132b530fb8e5c8474f1eea1d0ef69a374d5867d0300f7501803bffa55f7fad34f662d88a747ce73d552ec0f818722d2d5157cf8e5dc45a98fa552b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-unicode@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-normalize-unicode@npm:7.0.2"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/cb342f7507f28c8e9c500a2d6369c6b04a85f6c6f93aaa1ab6768d0e097453480834d3f7c5fad503f9fb9e178d9011df50ceaeebe2ac68d5daaa7c8a63ad3b3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-url@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-url@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/c5edca0646a13d76c5347fffaaa828184e035486d7eeb2a8b31781d30de6a90f7ad3f0cffe59e8fd4c31f1525fdb85b45777745685603ac533a151c42691f601
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-whitespace@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-normalize-whitespace@npm:7.0.0"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/c409362e3256ed66629fc48c63e834c9bfb598ca20587adb620bbc04fdccef4cd0d08b1f485eb8290d6a30e8dd836fecb0def38c3a49fe8503e2579e60f5bccf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-ordered-values@npm:^7.0.1":
|
||||
"postcss-normalize-charset@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-ordered-values@npm:7.0.1"
|
||||
dependencies:
|
||||
cssnano-utils: "npm:^5.0.0"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
resolution: "postcss-normalize-charset@npm:7.0.1"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/048082c09eee021d97def02eb8fc03fb0414402b1f6925af29a862f537b66b43d7a8e8d94c552ca67cd6172230873260f4ad44f1d5bac81c553afb054d80e6a8
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/bcec822491e3421b009c688473433164b5c80bbef48af4e47f704bee68f0b7ba2009aaf46788e698dd233d5f4e1cf444a4f59a901623c73f8458c2227b15db57
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-reduce-initial@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-reduce-initial@npm:7.0.2"
|
||||
"postcss-normalize-display-values@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-display-values@npm:7.0.1"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/53f341c17a5487639e6f7c917ad695e059bf4aff66b3c971e008163f774337444753310def9f38dd26066ea96b136422592fc74077c38c40b3bfdfaa338d5b58
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-positions@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-positions@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/72b23ab87c97c155d2ec475fba8a8b968f7c7b42d055a79b267449d570c328d5ea4cb0002428cf26e9daa70c58655e0b931d2a5801cc407554d3f03a21ac041b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-repeat-style@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-repeat-style@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/db677bceec8c00a1860b64932b99af937e7674b3e5c5ac333c95efb090e9abd747eca4ad51855f0fe73fbe544c3d21e58d06b39e03fd525945309743e31ec235
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-string@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-string@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/48df2eaca6f5365af31ad46fd60a32dc7b714cc5ec8ba80980e65855ddc47c03ac82077ce7ca04c90898f73d173410d1d6a104754ff487e7e5a59e3eae8325b3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-timing-functions@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-timing-functions@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/31fb88489244334295918fa7d6af2d76c310a83abd20be0a7f1c408c54ac0c0f81b0ae7877698bf66de1f76495766e159c8871387407dfcafa0cb1a53f5f0460
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-unicode@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-normalize-unicode@npm:7.0.3"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.24.5"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/fc10205655f77d6467da811fbd26aa607c519cbf162ae2ba40821cf64227233445490881119c820c6988c0943cb2f4dc755abe94cb30637001ca35cce5d07b61
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-url@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-url@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/975dd0d1b55b637d45756ec57e554b2134f77368dd3ae09be9fa6636f2f41e72422505409d7fca75c635b9b1b8ec8ec2607d84c6c85497bbfd4e7748a2992882
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-normalize-whitespace@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-normalize-whitespace@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/05a0fa74f4c8e93243053b9cc865cbddddb309b2ccb08271ca9c38ea7ece2ff43d5faa12cce87f06e40cbcf22c94443c9fa2b74ed0c6b94d72a9e67ea0381626
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-ordered-values@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-ordered-values@npm:7.0.2"
|
||||
dependencies:
|
||||
cssnano-utils: "npm:^5.0.1"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/be8fb13639fb0e1ffd7d4e9bb4824d3a283c8a63a8b0dd1a654435fff1e019007c79be877940bb101bb9ebd8ba3ac18bcffd144e939890bedeb40044dcc2b9cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-reduce-initial@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-reduce-initial@npm:7.0.3"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.24.5"
|
||||
caniuse-api: "npm:^3.0.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/5a8260cbf7fa6ea12908debe23e191bb45109b29048d15e63c60df42c4ed62c860273ce9b37172d5f31c4bdb965e984962e4e6f506939a1fc49202dd7bf520c5
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/8fd9ff4b49a2f7e1b7c51b7da637578e32a178363e3e932c80565241454dca306658dacd390ad3d73647d55dace8be8fe29278668afa32fd9d872ee7026bdbf7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-reduce-transforms@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-reduce-transforms@npm:7.0.0"
|
||||
"postcss-reduce-transforms@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-reduce-transforms@npm:7.0.1"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/1c369a1be820a80e8bf06376476190fe2ae5a0b5a7459257d7d9b5bc0c9aed79f46026e8558fca088f7a814e632c678f67749b246901a3839f2d50b7b9ec2d41
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/a22d07559859b9d4313d579104a25aa254695bc37dec5134de1064d1bd52b9d1f33f050fbf330170ef1105ede9aad7741bbcf9cad2221a6a5c8d529fd3cf0259
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25705,36 +25705,36 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-selector-parser@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "postcss-selector-parser@npm:7.0.0"
|
||||
"postcss-selector-parser@npm:^7.0.0, postcss-selector-parser@npm:^7.1.0":
|
||||
version: 7.1.0
|
||||
resolution: "postcss-selector-parser@npm:7.1.0"
|
||||
dependencies:
|
||||
cssesc: "npm:^3.0.0"
|
||||
util-deprecate: "npm:^1.0.2"
|
||||
checksum: 10/0e92be7281e2b440a8be8cf207de40a24ca7bc765577916499614d5a47827a3e658206728cc559db96803e554270516104aad919a04f91bfa8914ccef1ba14ca
|
||||
checksum: 10/2caf09e66e2be81d45538f8afdc5439298c89bea71e9943b364e69dce9443d9c5ab33f4dd8b237f1ed7d2f38530338dcc189c1219d888159e6afb5b0afe58b19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-svgo@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "postcss-svgo@npm:7.0.1"
|
||||
"postcss-svgo@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "postcss-svgo@npm:7.0.2"
|
||||
dependencies:
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
svgo: "npm:^3.3.2"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/4196d9b7ec37ea7c427b6d3d40fa75bdae6d1fdf5a814481202138fb9b074ecc1e442b8e0202aa8c76eaaff747e2f6bfec968cfe7bc774d8a58faf8bd945ff4e
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/8615877dffbac2bb2b971fb0e8c882ebff479c2529a0fc20937d09623fcaf35a2d934c4046188bae2534729aba1de5a1ba227630aaf96a800b6f2acdbfbf1d32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-unique-selectors@npm:^7.0.3":
|
||||
version: 7.0.3
|
||||
resolution: "postcss-unique-selectors@npm:7.0.3"
|
||||
"postcss-unique-selectors@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "postcss-unique-selectors@npm:7.0.4"
|
||||
dependencies:
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/c38ca6b5f539cae1e0e8ef0efa338f91e4e054dbd9c619e26708d787e94ce788739bbe782103f2cf35c38819233897901038292255a1726905bd04433ac9e5f2
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/b880f96fdb20037b16ae21b48f5240a4cf8585bf3133c7894dd869711b14f3a1a82bbdecd36adc78f8c34553a46fc2199ed3e92d5031b0267ff6f43894fc00f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25745,7 +25745,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:8.5.1, postcss@npm:^8.4.33, postcss@npm:^8.4.38, postcss@npm:^8.5.1":
|
||||
"postcss@npm:8.5.1":
|
||||
version: 8.5.1
|
||||
resolution: "postcss@npm:8.5.1"
|
||||
dependencies:
|
||||
@@ -25756,6 +25756,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.4.33, postcss@npm:^8.4.40, postcss@npm:^8.5.1":
|
||||
version: 8.5.6
|
||||
resolution: "postcss@npm:8.5.6"
|
||||
dependencies:
|
||||
nanoid: "npm:^3.3.11"
|
||||
picocolors: "npm:^1.1.1"
|
||||
source-map-js: "npm:^1.2.1"
|
||||
checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prefix-style@npm:2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "prefix-style@npm:2.0.1"
|
||||
@@ -30048,15 +30059,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylehacks@npm:^7.0.4":
|
||||
version: 7.0.4
|
||||
resolution: "stylehacks@npm:7.0.4"
|
||||
"stylehacks@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "stylehacks@npm:7.0.5"
|
||||
dependencies:
|
||||
browserslist: "npm:^4.23.3"
|
||||
postcss-selector-parser: "npm:^6.1.2"
|
||||
browserslist: "npm:^4.24.5"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.31
|
||||
checksum: 10/fc9d6b1e0b996d139a77f391df6db49ee1ab7e8fdeb32a8fa6b4c11512e72eb072470c32080171e46ebe123c9c96d763b9e4421b09c9c428985077940b6ba085
|
||||
postcss: ^8.4.32
|
||||
checksum: 10/798ac0f92ff4489c251550d64b903f1aa8b5946e5b09b33ebf68290b5a345257cecf98c989526a5d462b560081194fead38c4f804ec016ceb8b1b3f17ec74fc5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user