From b14263af458b371ac0f1716f05caac260119afc9 Mon Sep 17 00:00:00 2001 From: Polina Boneva <13227501+polibb@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:54:41 +0300 Subject: [PATCH] Dashboard: Remove old panel code and leave only new panel design (#74196) * delete PanelHeader * PanelHeaderMenuItem is only used in PageHeader * PanelHeaderCorner only used by PanelEditorTableView * PanelHeaderMenuTrigger not used anywhere * PanelHeaderMenuWrapperNew is PanelHeaderMenuWrapper, old one is deleted * remove newPanelChromeUI from test * delete feature flag newPanelChromeUI * PanelHeaderMenuWrapperNew is PanelHeaderMenuWrapper, old one is deleted --- .betterer.results | 12 +- .../feature-toggles/index.md | 1 - .../src/types/featureToggles.gen.ts | 1 - .../src/selectors/components.ts | 3 +- .../components/PanelChrome/PanelStatus.tsx | 2 + pkg/services/featuremgmt/registry.go | 8 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - .../core/components/PageHeader/PageHeader.tsx | 2 +- .../PageHeader}/PanelHeaderMenuItem.tsx | 0 .../PanelEditor/PanelEditorTableView.tsx | 2 +- .../PanelEditor}/PanelHeaderCorner.test.tsx | 0 .../PanelEditor}/PanelHeaderCorner.tsx | 0 .../containers/PublicDashboardPage.test.tsx | 1 - .../dashboard/dashgrid/PanelChromeAngular.tsx | 111 +++------ .../dashgrid/PanelHeader/PanelHeader.test.tsx | 47 ---- .../dashgrid/PanelHeader/PanelHeader.tsx | 109 --------- .../PanelHeaderLoadingIndicator.tsx | 53 ----- .../dashgrid/PanelHeader/PanelHeaderMenu.tsx | 65 +---- .../PanelHeader/PanelHeaderMenuTrigger.tsx | 58 ----- .../PanelHeader/PanelHeaderMenuWrapper.tsx | 28 +-- .../dashgrid/PanelStateWrapper.test.tsx | 4 +- .../dashboard/dashgrid/PanelStateWrapper.tsx | 224 ++++-------------- 23 files changed, 97 insertions(+), 639 deletions(-) rename public/app/{features/dashboard/dashgrid/PanelHeader => core/components/PageHeader}/PanelHeaderMenuItem.tsx (100%) rename public/app/features/dashboard/{dashgrid/PanelHeader => components/PanelEditor}/PanelHeaderCorner.test.tsx (100%) rename public/app/features/dashboard/{dashgrid/PanelHeader => components/PanelEditor}/PanelHeaderCorner.tsx (100%) delete mode 100644 public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx delete mode 100644 public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx delete mode 100644 public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderLoadingIndicator.tsx delete mode 100644 public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx diff --git a/.betterer.results b/.betterer.results index c43b7369f1a..82af97cb0ac 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1405,6 +1405,9 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "82"], [0, 0, 0, "Unexpected any. Specify a different type.", "83"] ], + "public/app/core/components/PageHeader/PanelHeaderMenuItem.tsx:5381": [ + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + ], "public/app/core/components/PasswordField/PasswordField.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], @@ -2091,17 +2094,10 @@ exports[`better eslint`] = { "public/app/features/dashboard/dashgrid/DashboardPanel.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] - ], - "public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] - ], "public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 4a442782435..a65b893f3e5 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -27,7 +27,6 @@ Some features are enabled by default. You can disable these feature by setting t | `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | | `redshiftAsyncQueryDataSupport` | Enable async query data support for Redshift | Yes | | `athenaAsyncQueryDataSupport` | Enable async query data support for Athena | Yes | -| `newPanelChromeUI` | Show updated look and feel of grafana-ui PanelChrome: panel header, icons, and menu | Yes | | `nestedFolderPicker` | Enables the new folder picker to work with nested folders. Requires the nestedFolders feature flag | Yes | | `accessTokenExpirationCheck` | Enable OAuth access_token expiration check and token refresh using the refresh_token | | | `emptyDashboardPage` | Enable the redesigned user interface of a dashboard page that includes no panels | Yes | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 188c660696c..7ae86dac25f 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -46,7 +46,6 @@ export interface FeatureToggles { cloudWatchCrossAccountQuerying?: boolean; redshiftAsyncQueryDataSupport?: boolean; athenaAsyncQueryDataSupport?: boolean; - newPanelChromeUI?: boolean; showDashboardValidationWarnings?: boolean; mysqlAnsiQuotes?: boolean; accessControlOnCall?: boolean; diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 4086948debf..5878c0d472c 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -72,11 +72,12 @@ export const Components = { Panels: { Panel: { title: (title: string) => `data-testid Panel header ${title}`, - headerItems: (item: string) => `Panel header item ${item}`, + headerItems: (item: string) => `data-testid Panel header item ${item}`, menuItems: (item: string) => `data-testid Panel menu item ${item}`, menu: (title: string) => `data-testid Panel menu ${title}`, containerByTitle: (title: string) => `${title} panel`, headerCornerInfo: (mode: string) => `Panel header ${mode}`, + status: (status: string) => `data-testid Panel status ${status}`, loadingBar: () => `Panel loading bar`, HoverWidget: { container: 'data-testid hover-header-container', diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx index 4f0c9cb887a..9b92be7a9b3 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelStatus.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { useStyles2 } from '../../themes'; import { ToolbarButton } from '../ToolbarButton/ToolbarButton'; @@ -24,6 +25,7 @@ export function PanelStatus({ message, onClick, ariaLabel = 'status' }: Props) { iconSize="md" tooltip={message || ''} aria-label={ariaLabel} + data-testid={selectors.components.Panels.Panel.status('error')} /> ); } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 3fab74be7f6..44c617df8e0 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -196,14 +196,6 @@ var ( FrontendOnly: true, Owner: awsDatasourcesSquad, }, - { - Name: "newPanelChromeUI", - Description: "Show updated look and feel of grafana-ui PanelChrome: panel header, icons, and menu", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaDashboardsSquad, - }, { Name: "showDashboardValidationWarnings", Description: "Show warnings when dashboards do not validate against the schema", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index a0c338020a1..780faa9780d 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -27,7 +27,6 @@ entityStore,experimental,@grafana/grafana-app-platform-squad,true,false,false,fa cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,false,false,false,false redshiftAsyncQueryDataSupport,GA,@grafana/aws-datasources,false,false,false,false athenaAsyncQueryDataSupport,GA,@grafana/aws-datasources,false,false,false,true -newPanelChromeUI,GA,@grafana/dashboards-squad,false,false,false,true showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,false,false,false,false mysqlAnsiQuotes,experimental,@grafana/backend-platform,false,false,false,false accessControlOnCall,preview,@grafana/grafana-authnz-team,false,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index e11d207b2bd..4756def629a 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -119,10 +119,6 @@ const ( // Enable async query data support for Athena FlagAthenaAsyncQueryDataSupport = "athenaAsyncQueryDataSupport" - // FlagNewPanelChromeUI - // Show updated look and feel of grafana-ui PanelChrome: panel header, icons, and menu - FlagNewPanelChromeUI = "newPanelChromeUI" - // FlagShowDashboardValidationWarnings // Show warnings when dashboards do not validate against the schema FlagShowDashboardValidationWarnings = "showDashboardValidationWarnings" diff --git a/public/app/core/components/PageHeader/PageHeader.tsx b/public/app/core/components/PageHeader/PageHeader.tsx index c1404c7a2ec..12c80a204f9 100644 --- a/public/app/core/components/PageHeader/PageHeader.tsx +++ b/public/app/core/components/PageHeader/PageHeader.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { NavModelItem, GrafanaTheme2 } from '@grafana/data'; import { Tab, TabsBar, Icon, useStyles2, toIconName } from '@grafana/ui'; -import { PanelHeaderMenuItem } from 'app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem'; +import { PanelHeaderMenuItem } from 'app/core/components/PageHeader/PanelHeaderMenuItem'; import { PageInfoItem } from '../Page/types'; import { PageInfo } from '../PageInfo/PageInfo'; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx b/public/app/core/components/PageHeader/PanelHeaderMenuItem.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx rename to public/app/core/components/PageHeader/PanelHeaderMenuItem.tsx diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx index 12b06b511a5..aa006ad53cb 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx @@ -6,10 +6,10 @@ import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; import { PanelRenderer } from 'app/features/panel/components/PanelRenderer'; import { Options } from 'app/plugins/panel/table/panelcfg.gen'; -import PanelHeaderCorner from '../../dashgrid/PanelHeader/PanelHeaderCorner'; import { getTimeSrv } from '../../services/TimeSrv'; import { DashboardModel, PanelModel } from '../../state'; +import PanelHeaderCorner from './PanelHeaderCorner'; import { usePanelLatestData } from './usePanelLatestData'; export interface Props { diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.test.tsx b/public/app/features/dashboard/components/PanelEditor/PanelHeaderCorner.test.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.test.tsx rename to public/app/features/dashboard/components/PanelEditor/PanelHeaderCorner.test.tsx diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/components/PanelEditor/PanelHeaderCorner.tsx similarity index 100% rename from public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx rename to public/app/features/dashboard/components/PanelEditor/PanelHeaderCorner.tsx diff --git a/public/app/features/dashboard/containers/PublicDashboardPage.test.tsx b/public/app/features/dashboard/containers/PublicDashboardPage.test.tsx index e9076a91655..920a6781b1d 100644 --- a/public/app/features/dashboard/containers/PublicDashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/PublicDashboardPage.test.tsx @@ -134,7 +134,6 @@ const dashboardBase = { describe('PublicDashboardPage', () => { beforeEach(() => { config.featureToggles.publicDashboards = true; - config.featureToggles.newPanelChromeUI = true; jest.clearAllMocks(); }); diff --git a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx index ced559cae25..ee81841799e 100644 --- a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx @@ -1,11 +1,9 @@ -import classNames from 'classnames'; import React, { PureComponent } from 'react'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { Subscription } from 'rxjs'; import { getDefaultTimeRange, LoadingState, PanelData, PanelPlugin } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; -import { AngularComponent, getAngularLoader, locationService } from '@grafana/runtime'; +import { AngularComponent, getAngularLoader } from '@grafana/runtime'; import { PanelChrome } from '@grafana/ui'; import config from 'app/core/config'; import { PANEL_BORDER } from 'app/core/constants'; @@ -13,13 +11,11 @@ import { setPanelAngularComponent } from 'app/features/panel/state/reducers'; import { getPanelStateForModel } from 'app/features/panel/state/selectors'; import { StoreState } from 'app/types'; -import { isSoloRoute } from '../../../routes/utils'; import { getTimeSrv, TimeSrv } from '../services/TimeSrv'; import { DashboardModel, PanelModel } from '../state'; import { getPanelChromeProps } from '../utils/getPanelChromeProps'; -import { PanelHeader } from './PanelHeader/PanelHeader'; -import { PanelHeaderMenuWrapperNew } from './PanelHeader/PanelHeaderMenuWrapper'; +import { PanelHeaderMenuWrapper } from './PanelHeader/PanelHeaderMenuWrapper'; interface OwnProps { panel: PanelModel; @@ -180,85 +176,44 @@ export class PanelChromeAngularUnconnected extends PureComponent { } render() { - const { dashboard, panel, isViewing, isEditing, plugin } = this.props; + const { dashboard, panel } = this.props; const { errorMessage, data } = this.state; const { transparent } = panel; - const alertState = data.alertState?.state; const panelChromeProps = getPanelChromeProps({ ...this.props, data }); - const containerClassNames = classNames({ - 'panel-container': true, - 'panel-container--absolute': isSoloRoute(locationService.getLocation().pathname), - 'panel-container--transparent': transparent, - 'panel-container--no-title': this.hasOverlayHeader(), - 'panel-has-alert': panel.alert !== undefined, - [`panel-alert-state--${alertState}`]: alertState !== undefined, - }); + // Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav + const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined; - const panelContentClassNames = classNames({ - 'panel-content': true, - 'panel-content--no-padding': plugin.noPadding, - }); + const menu = ( +
+ +
+ ); - if (config.featureToggles.newPanelChromeUI) { - // Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav - const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined; - - const menu = ( -
- -
- ); - - return ( - - {() =>
(this.element = element)} className="panel-height-helper" />} - - ); - } else { - return ( -
- -
-
(this.element = element)} className="panel-height-helper" /> -
-
- ); - } + return ( + + {() =>
(this.element = element)} className="panel-height-helper" />} + + ); } } diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx deleted file mode 100644 index fa1ea16bdac..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import { config } from '@grafana/runtime'; - -import { createEmptyQueryResponse } from '../../../explore/state/utils'; -import { PanelModel } from '../../state'; -import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; - -import { PanelHeader } from './PanelHeader'; - -let panelModel = new PanelModel({ - id: 1, - gridPos: { x: 1, y: 1, w: 1, h: 1 }, - type: 'type', - title: 'title', -}); - -let panelData = createEmptyQueryResponse(); - -describe('Panel Header', () => { - const dashboardModel = createDashboardModelFixture({}, {}); - it('will render header title but not render dropdown icon when dashboard is being viewed publicly', () => { - window.history.pushState({}, 'Test Title', '/public-dashboards/abc123'); - config.publicDashboardAccessToken = 'abc123'; - - render( - - ); - - expect(screen.getByText('title')).toBeDefined(); - expect(screen.queryByTestId('panel-dropdown')).toBeNull(); - }); - - it('will render header title and dropdown icon when dashboard is not being viewed publicly', () => { - const dashboardModel = createDashboardModelFixture({}, {}); - window.history.pushState({}, 'Test Title', '/d/abc/123'); - config.publicDashboardAccessToken = ''; - - render( - - ); - - expect(screen.getByText('title')).toBeDefined(); - expect(screen.getByTestId('panel-dropdown')).toBeDefined(); - }); -}); diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx deleted file mode 100644 index 36610b50921..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { css, cx } from '@emotion/css'; -import React from 'react'; - -import { DataLink, GrafanaTheme2, PanelData } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; -import { config, reportInteraction } from '@grafana/runtime'; -import { Icon, useStyles2, ClickOutsideWrapper } from '@grafana/ui'; -import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; -import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; - -import PanelHeaderCorner from './PanelHeaderCorner'; -import { PanelHeaderLoadingIndicator } from './PanelHeaderLoadingIndicator'; -import { PanelHeaderMenuTrigger } from './PanelHeaderMenuTrigger'; -import { PanelHeaderMenuWrapper } from './PanelHeaderMenuWrapper'; -import { PanelHeaderNotices } from './PanelHeaderNotices'; - -export interface Props { - panel: PanelModel; - dashboard: DashboardModel; - title?: string; - description?: string; - links?: DataLink[]; - error?: string; - alertState?: string; - isViewing: boolean; - isEditing: boolean; - data: PanelData; -} - -export function PanelHeader({ panel, error, isViewing, isEditing, data, alertState, dashboard }: Props) { - const onCancelQuery = () => panel.getQueryRunner().cancelQuery(); - const title = panel.getDisplayTitle(); - const className = cx('panel-header', !(isViewing || isEditing) ? 'grid-drag-handle' : ''); - const styles = useStyles2(panelStyles); - - const onOpenMenu = () => { - reportInteraction('dashboards_panelheader_menu', { item: 'menu' }); - }; - - return ( - <> - - -
- - {({ closeMenu, panelMenuOpen }) => { - return ( - -
- - {alertState ? ( - - ) : null} -

{title}

- {!config.publicDashboardAccessToken && ( -
- - {panelMenuOpen ? : null} -
- )} - {data.request && data.request.timeInfo && ( - - {data.request.timeInfo} - - )} -
-
- ); - }} -
-
- - ); -} - -const panelStyles = (theme: GrafanaTheme2) => { - return { - titleText: css` - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - max-width: calc(100% - 38px); - cursor: pointer; - font-weight: ${theme.typography.fontWeightMedium}; - font-size: ${theme.typography.body.fontSize}; - margin: 0; - - &:hover { - color: ${theme.colors.text.primary}; - } - .panel-has-alert & { - max-width: calc(100% - 54px); - } - `, - }; -}; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderLoadingIndicator.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderLoadingIndicator.tsx deleted file mode 100644 index 240549f3ccb..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderLoadingIndicator.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { GrafanaTheme2, LoadingState } from '@grafana/data'; -import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; - -interface Props { - state: LoadingState; - onClick: () => void; -} - -export const PanelHeaderLoadingIndicator = ({ state, onClick }: Props) => { - const styles = useStyles2(getStyles); - - if (state === LoadingState.Loading) { - return ( - // TODO: fix keyboard a11y - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
- - - -
- ); - } - - if (state === LoadingState.Streaming) { - return ( - // TODO: fix keyboard a11y - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
-
-
- ); - } - - return null; -}; - -function getStyles(theme: GrafanaTheme2) { - return { - streamIndicator: css` - width: 10px; - height: 10px; - background: ${theme.colors.text.disabled}; - box-shadow: 0 0 2px ${theme.colors.text.disabled}; - border-radius: ${theme.shape.radius.circle}; - position: relative; - top: 6px; - right: 1px; - `, - }; -} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx index b32a6f67228..78a6ce46343 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx @@ -1,12 +1,9 @@ -import classnames from 'classnames'; -import React, { PureComponent } from 'react'; +import React from 'react'; import { PanelMenuItem } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Menu } from '@grafana/ui'; -import { PanelHeaderMenuItem } from './PanelHeaderMenuItem'; - export interface Props { items: PanelMenuItem[]; style?: React.CSSProperties; @@ -14,65 +11,7 @@ export interface Props { className?: string; } -export class PanelHeaderMenu extends PureComponent { - renderItems = (menu: PanelMenuItem[], isSubMenu = false) => { - return ( -
    - {menu.map((menuItem, idx: number) => { - return ( - - {menuItem.subMenu && this.renderItems(menuItem.subMenu, true)} - - ); - })} -
- ); - }; - - render() { - return ( -
- {this.renderItems(flattenGroups(this.props.items))} -
- ); - } -} - -function flattenGroups(items: PanelMenuItem[]): PanelMenuItem[] { - return items.reduce((all: PanelMenuItem[], item) => { - if (Array.isArray(item.subMenu) && item.type === 'submenu') { - all.push({ - ...item, - subMenu: flattenGroups(item.subMenu), - }); - return all; - } - - if (Array.isArray(item.subMenu) && item.type === 'group') { - const { subMenu, ...rest } = item; - all.push(rest); - all.push.apply(all, flattenGroups(subMenu)); - return all; - } - - all.push(item); - return all; - }, []); -} - -export function PanelHeaderMenuNew({ items }: Props) { +export function PanelHeaderMenu({ items }: Props) { const renderItems = (items: PanelMenuItem[]) => { return items.map((item) => { switch (item.type) { diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx deleted file mode 100644 index 881d3f9f2a6..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { HTMLAttributes, MouseEvent, ReactElement, useCallback, useRef, useState } from 'react'; - -import { CartesianCoords2D } from '@grafana/data'; - -interface PanelHeaderMenuTriggerApi { - panelMenuOpen: boolean; - closeMenu: () => void; -} - -interface Props extends Omit, 'children'> { - children: (props: PanelHeaderMenuTriggerApi) => ReactElement; - onOpenMenu?: () => void; -} - -export function PanelHeaderMenuTrigger({ children, onOpenMenu, ...divProps }: Props) { - const clickCoordinates = useRef({ x: 0, y: 0 }); - const [panelMenuOpen, setPanelMenuOpen] = useState(false); - - const onMenuToggle = useCallback( - (event: MouseEvent) => { - if (!isClick(clickCoordinates.current, eventToClickCoordinates(event))) { - return; - } - - setPanelMenuOpen(!panelMenuOpen); - if (panelMenuOpen) { - onOpenMenu?.(); - } - }, - [panelMenuOpen, setPanelMenuOpen, onOpenMenu] - ); - - const onMouseDown = useCallback((event: MouseEvent) => { - clickCoordinates.current = eventToClickCoordinates(event); - }, []); - - return ( - // TODO: fix keyboard a11y - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
- {children({ panelMenuOpen, closeMenu: () => setPanelMenuOpen(false) })} -
- ); -} - -function isClick(current: CartesianCoords2D, clicked: CartesianCoords2D, deadZone = 3.5): boolean { - // A "deadzone" radius is added so that if the cursor is moved within this radius - // between mousedown and mouseup, it's still considered a click and not a drag. - const clickDistance = Math.sqrt((current.x - clicked.x) ** 2 + (current.y - clicked.y) ** 2); - return clickDistance <= deadZone; -} - -function eventToClickCoordinates(event: MouseEvent): CartesianCoords2D { - return { - x: event.clientX, - y: event.clientY, - }; -} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuWrapper.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuWrapper.tsx index bdda1600431..d769112ba99 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuWrapper.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuWrapper.tsx @@ -4,7 +4,7 @@ import { LoadingState } from '@grafana/data'; import { DashboardModel, PanelModel } from '../../state'; -import { PanelHeaderMenu, PanelHeaderMenuNew } from './PanelHeaderMenu'; +import { PanelHeaderMenu } from './PanelHeaderMenu'; import { PanelHeaderMenuProvider } from './PanelHeaderMenuProvider'; interface Props { @@ -16,32 +16,10 @@ interface Props { menuWrapperClassName?: string; } -export function PanelHeaderMenuWrapper({ - panel, - dashboard, - loadingState, - style, - menuItemsClassName, - menuWrapperClassName, -}: Props) { +export function PanelHeaderMenuWrapper({ style, panel, dashboard, loadingState }: Props) { return ( - {({ items }) => ( - - )} - - ); -} - -export function PanelHeaderMenuWrapperNew({ style, panel, dashboard, loadingState }: Props) { - return ( - - {({ items }) => } + {({ items }) => } ); } diff --git a/public/app/features/dashboard/dashgrid/PanelStateWrapper.test.tsx b/public/app/features/dashboard/dashgrid/PanelStateWrapper.test.tsx index 3a49af6701f..8b6be6c4b98 100644 --- a/public/app/features/dashboard/dashgrid/PanelStateWrapper.test.tsx +++ b/public/app/features/dashboard/dashgrid/PanelStateWrapper.test.tsx @@ -128,9 +128,7 @@ describe('PanelStateWrapper', () => { ); - const button = screen.getByRole('button', { - name: selectors.components.Panels.Panel.headerCornerInfo('error'), - }); + const button = screen.getByTestId(selectors.components.Panels.Panel.status('error')); expect(button).toBeInTheDocument(); await act(async () => { fireEvent.focus(button); diff --git a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx index 067f92a9eea..6bf76c3d075 100644 --- a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx +++ b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx @@ -1,4 +1,3 @@ -import classNames from 'classnames'; import React, { PureComponent } from 'react'; import { Subscription } from 'rxjs'; @@ -17,13 +16,11 @@ import { PanelData, PanelPlugin, PanelPluginMeta, - PluginContextProvider, TimeRange, toDataFrameDTO, toUtc, } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; -import { config, locationService, RefreshEvent } from '@grafana/runtime'; +import { RefreshEvent } from '@grafana/runtime'; import { VizLegendOptions } from '@grafana/schema'; import { ErrorBoundary, @@ -33,7 +30,6 @@ import { SeriesVisibilityChangeMode, AdHocFilterItem, } from '@grafana/ui'; -import { PANEL_BORDER } from 'app/core/constants'; import { profiler } from 'app/core/profiler'; import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; @@ -43,7 +39,6 @@ import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/ove import { dispatch } from 'app/store/store'; import { RenderEvent } from 'app/types/events'; -import { isSoloRoute } from '../../../routes/utils'; import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annotations/api'; import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner'; import { getTimeSrv, TimeSrv } from '../services/TimeSrv'; @@ -51,8 +46,7 @@ import { DashboardModel, PanelModel } from '../state'; import { getPanelChromeProps } from '../utils/getPanelChromeProps'; import { loadSnapshotData } from '../utils/loadSnapshotData'; -import { PanelHeader } from './PanelHeader/PanelHeader'; -import { PanelHeaderMenuWrapperNew } from './PanelHeader/PanelHeaderMenuWrapper'; +import { PanelHeaderMenuWrapper } from './PanelHeader/PanelHeaderMenuWrapper'; import { seriesVisibilityConfigFactory } from './SeriesVisibilityConfigFactory'; import { liveTimer } from './liveTimer'; @@ -523,181 +517,59 @@ export class PanelStateWrapper extends PureComponent { ); } - renderPanel(width: number, height: number) { - const { panel, plugin, dashboard } = this.props; - const { renderCounter, data } = this.state; - const { theme } = config; - const { state: loadingState } = data; - - // do not render component until we have first data - if (this.skipFirstRender(loadingState)) { - return null; - } - - // This is only done to increase a counter that is used by backend - // image rendering to know when to capture image - if (this.shouldSignalRenderingCompleted(loadingState, plugin.meta)) { - profiler.renderingCompleted(); - } - - const PanelComponent = plugin.panel!; - const timeRange = this.state.liveTime ?? data.timeRange ?? this.timeSrv.timeRange(); - const headerHeight = this.hasOverlayHeader() ? 0 : theme.panelHeaderHeight; - const chromePadding = plugin.noPadding ? 0 : theme.panelPadding; - const panelWidth = width - chromePadding * 2 - PANEL_BORDER; - const innerPanelHeight = height - headerHeight - chromePadding * 2 - PANEL_BORDER; - const panelContentClassNames = classNames({ - 'panel-content': true, - 'panel-content--no-padding': plugin.noPadding, - }); - const panelOptions = panel.getOptions(); - - // Update the event filter (dashboard settings may have changed) - // Yes this is called ever render for a function that is triggered on every mouse move - this.eventFilter.onlyLocal = dashboard.graphTooltip === 0; - - const timeZone = this.props.timezone || this.props.dashboard.getTimezone(); - - return ( - <> -
- - - - - -
- - ); - } - - hasOverlayHeader() { - const { panel } = this.props; - const { data } = this.state; - - // always show normal header if we have time override - if (data.request && data.request.timeInfo) { - return false; - } - - return !panel.hasTitle(); - } - render() { - const { dashboard, panel, isViewing, isEditing, width, height, plugin } = this.props; + const { dashboard, panel, width, height, plugin } = this.props; const { errorMessage, data } = this.state; const { transparent } = panel; - const alertState = data.alertState?.state; - const hasHoverHeader = this.hasOverlayHeader(); - - const containerClassNames = classNames({ - 'panel-container': true, - 'panel-container--absolute': isSoloRoute(locationService.getLocation().pathname), - 'panel-container--transparent': transparent, - 'panel-container--no-title': hasHoverHeader, - [`panel-alert-state--${alertState}`]: alertState !== undefined, - }); - const panelChromeProps = getPanelChromeProps({ ...this.props, data }); - if (config.featureToggles.newPanelChromeUI) { - // Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav - const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined; + // Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav + const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined; - const menu = ( -
- -
- ); + const menu = ( +
+ +
+ ); - return ( - - {(innerWidth, innerHeight) => ( - <> - - {({ error }) => { - if (error) { - return null; - } - return this.renderPanelContent(innerWidth, innerHeight); - }} - - - )} - - ); - } else { - return ( -
- - - {({ error }) => { - if (error) { - return null; - } - return this.renderPanel(width, height); - }} - -
- ); - } + return ( + + {(innerWidth, innerHeight) => ( + <> + + {({ error }) => { + if (error) { + return null; + } + return this.renderPanelContent(innerWidth, innerHeight); + }} + + + )} + + ); } }