From 2f1ea7dc98543ad2b08a04ae82637a2d894984f3 Mon Sep 17 00:00:00 2001 From: Maxim Mordasov Date: Tue, 30 Apr 2024 14:25:31 +0100 Subject: [PATCH] Fix tabs navigation on the settings page (#4289) # What this PR does Fix tabs navigation on the settings page ## Which issue(s) this PR closes Related to https://github.com/grafana/oncall-private/issues/2563 ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --------- Co-authored-by: Dominik Broj --- .../e2e-tests/settings/tabs.test.ts | 12 +++++++++++ grafana-plugin/e2e-tests/utils/navigation.ts | 7 +++++-- .../src/pages/settings/SettingsPage.tsx | 21 ++++++++++++------- .../pages/settings/tabs/ChatOps/ChatOps.tsx | 6 +++--- 4 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 grafana-plugin/e2e-tests/settings/tabs.test.ts diff --git a/grafana-plugin/e2e-tests/settings/tabs.test.ts b/grafana-plugin/e2e-tests/settings/tabs.test.ts new file mode 100644 index 00000000..c878e0b0 --- /dev/null +++ b/grafana-plugin/e2e-tests/settings/tabs.test.ts @@ -0,0 +1,12 @@ +import { test, expect } from '../fixtures'; +import { goToOnCallPage } from '../utils/navigation'; + +test(`tab query param is used to show proper page tab`, async ({ adminRolePage }) => { + const { page } = adminRolePage; + goToOnCallPage(page, `settings`, { tab: 'ChatOps' }); + + const tab = page.getByRole('tab', { name: 'Chat Ops' }); + const isSelected = await tab.getAttribute('aria-selected'); + + expect(isSelected).toBe('true'); +}); diff --git a/grafana-plugin/e2e-tests/utils/navigation.ts b/grafana-plugin/e2e-tests/utils/navigation.ts index af9435df..2c53125a 100644 --- a/grafana-plugin/e2e-tests/utils/navigation.ts +++ b/grafana-plugin/e2e-tests/utils/navigation.ts @@ -1,4 +1,6 @@ +import { KeyValue } from '@grafana/data'; import type { Page } from '@playwright/test'; +import qs from 'query-string'; import { BASE_URL } from './constants'; @@ -17,8 +19,9 @@ const _goToPage = async (page: Page, url = '') => page.goto(`${BASE_URL}${url}`) export const goToGrafanaPage = async (page: Page, url = '') => _goToPage(page, url); -export const goToOnCallPage = async (page: Page, onCallPage: OnCallPage) => { - await _goToPage(page, `/a/grafana-oncall-app/${onCallPage}`); +export const goToOnCallPage = async (page: Page, onCallPage: OnCallPage, queryParams?: KeyValue) => { + const queryParamsString = queryParams ? `?${qs.stringify(queryParams)}` : ''; + await _goToPage(page, `/a/grafana-oncall-app/${onCallPage}${queryParamsString}`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); }; diff --git a/grafana-plugin/src/pages/settings/SettingsPage.tsx b/grafana-plugin/src/pages/settings/SettingsPage.tsx index 4d2926f5..71828696 100644 --- a/grafana-plugin/src/pages/settings/SettingsPage.tsx +++ b/grafana-plugin/src/pages/settings/SettingsPage.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import { AppRootProps } from '@grafana/data'; import { Tab, TabsBar } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -8,8 +9,9 @@ import { ChatOpsPage } from 'pages/settings/tabs/ChatOps/ChatOps'; import { MainSettings } from 'pages/settings/tabs/MainSettings/MainSettings'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { AppFeature } from 'state/features'; -import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore'; +import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; +import { LocationHelper } from 'utils/LocationHelper'; import { isUserActionAllowed, UserActions } from 'utils/authorization/authorization'; import { SettingsPageTab } from './SettingsPage.types'; @@ -21,18 +23,22 @@ import styles from './SettingsPage.module.css'; const cx = cn.bind(styles); -interface SettingsPageProps { - store: RootBaseStore; -} +interface SettingsPageProps extends AppRootProps, WithStoreProps {} interface SettingsPageState { activeTab: string; } @observer class Settings extends React.Component { - state: SettingsPageState = { - activeTab: SettingsPageTab.MainSettings.key, // should read from route instead - }; + constructor(props: SettingsPageProps) { + super(props); + + const tab = LocationHelper.getQueryParam('tab'); + + this.state = { + activeTab: tab || SettingsPageTab.MainSettings.key, + }; + } render() { return
{this.renderContent()}
; @@ -44,6 +50,7 @@ class Settings extends React.Component { const onTabChange = (tab: string) => { this.setState({ activeTab: tab }); + LocationHelper.update({ tab }, 'partial'); }; const hasLiveSettings = store.hasFeature(AppFeature.LiveSettings); diff --git a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx index ab89488c..b43e7510 100644 --- a/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx +++ b/grafana-plugin/src/pages/settings/tabs/ChatOps/ChatOps.tsx @@ -36,9 +36,9 @@ export class _ChatOpsPage extends React.Component { }; componentDidMount() { - const { query } = this.props; // eslint-disable-line + const tab = LocationHelper.getQueryParam('chatOpsTab'); - this.handleChatopsTabChange(query?.tab || ChatOpsTab.Slack); + this.handleChatopsTabChange(tab || ChatOpsTab.Slack); } componentWillUnmount() { @@ -94,7 +94,7 @@ export class _ChatOpsPage extends React.Component { handleChatopsTabChange(tab: ChatOpsTab) { this.setState({ activeTab: tab }); - LocationHelper.update({ tab: tab }, 'partial'); + LocationHelper.update({ chatOpsTab: tab }, 'partial'); } }