]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18776 Change the sub navigation menu
authorKevin Silva <kevin.silva@sonarsource.com>
Tue, 21 Mar 2023 14:11:45 +0000 (15:11 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 27 Mar 2023 20:03:03 +0000 (20:03 +0000)
12 files changed:
server/sonar-web/design-system/src/components/NavBarTabs.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/MainMenuItem-test.tsx [deleted file]
server/sonar-web/design-system/src/components/__tests__/Menu-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/MenuItem-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/NavBarTabs-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/icons/ChevronDownIcon.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/icons/index.ts
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/design-system/src/theme/light.ts
server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
server/sonar-web/src/main/js/components/ui/NavBarTabs.tsx

diff --git a/server/sonar-web/design-system/src/components/NavBarTabs.tsx b/server/sonar-web/design-system/src/components/NavBarTabs.tsx
new file mode 100644 (file)
index 0000000..669f2d3
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import styled from '@emotion/styled';
+import classNames from 'classnames';
+import tw, { theme } from 'twin.macro';
+import { themeBorder, themeColor, themeContrast } from '../helpers/theme';
+import { isDefined } from '../helpers/types';
+import ChevronDownIcon from './icons/ChevronDownIcon';
+import NavLink, { NavLinkProps } from './NavLink';
+import Tooltip from './Tooltip';
+
+interface Props extends React.HTMLAttributes<HTMLUListElement> {
+  children?: React.ReactNode;
+  className?: string;
+}
+
+export function NavBarTabs({ children, className, ...other }: Props) {
+  return (
+    <ul className={classNames('sw-flex sw-items-end sw-gap-6', className)} {...other}>
+      {children}
+    </ul>
+  );
+}
+
+interface NavBarTabLinkProps extends Omit<NavLinkProps, 'children'> {
+  active?: boolean;
+  children?: React.ReactNode;
+  text: string;
+  withChevron?: boolean;
+}
+
+export function NavBarTabLink(props: NavBarTabLinkProps) {
+  const { active, children, text, withChevron = false, ...linkProps } = props;
+  return (
+    <NavBarTabLinkWrapper>
+      <NavLink
+        className={({ isActive }) =>
+          classNames('sw-flex sw-items-center', { active: isDefined(active) ? active : isActive })
+        }
+        {...linkProps}
+      >
+        <span className="sw-inline-block sw-text-center" data-text={text}>
+          {text}
+        </span>
+        {children}
+        {withChevron && <ChevronDownIcon className="sw-ml-1" />}
+      </NavLink>
+    </NavBarTabLinkWrapper>
+  );
+}
+
+export function DisabledTabLink(props: { label: string; overlay: React.ReactNode }) {
+  return (
+    <NavBarTabLinkWrapper>
+      <Tooltip overlay={props.overlay}>
+        <a aria-disabled="true" className="disabled-link" role="link">
+          {props.label}
+        </a>
+      </Tooltip>
+    </NavBarTabLinkWrapper>
+  );
+}
+
+// Styling for <NavLink> due to its special className function, it conflicts when styled with Emotion.
+const NavBarTabLinkWrapper = styled.li`
+  & > a {
+    ${tw`sw-pb-3`};
+    ${tw`sw-block`};
+    ${tw`sw-box-border`};
+    ${tw`sw-transition-none`};
+    ${tw`sw-body-md`};
+    color: ${themeContrast('buttonSecondary')};
+    text-decoration: none;
+    border-bottom: ${themeBorder('xsActive', 'transparent')};
+    padding-bottom: calc(${theme('spacing.3')} + 1px); // 12px spacing + 3px border + 1px = 16px
+  }
+  & > a.active,
+  & > a:active,
+  & > a:hover,
+  & > a:focus {
+    border-bottom-color: ${themeColor('tabBorder')};
+  }
+  & > a.active > span[data-text],
+  & > a:active > span {
+    ${tw`sw-body-md-highlight`};
+  }
+  // This is a hack to have a link take the space of the bold font, so when active other ones are not moving
+  & > a > span[data-text]::before {
+    ${tw`sw-block`};
+    ${tw`sw-body-md-highlight`};
+    ${tw`sw-h-0`};
+    ${tw`sw-overflow-hidden`};
+    ${tw`sw-invisible`};
+    content: attr(data-text);
+  }
+  &:has(a.disabled-link) > a,
+  &:has(a.disabled-link) > a:hover,
+  &:has(a.disabled-link) > a.hover,
+  &:has(a.disabled-link)[aria-expanded='true'] {
+    ${tw`sw-cursor-default`};
+    border-bottom: ${themeBorder('xsActive', 'transparent', 1)};
+    color: ${themeContrast('subnavigationDisabled')};
+  }
+`;
diff --git a/server/sonar-web/design-system/src/components/__tests__/MainMenuItem-test.tsx b/server/sonar-web/design-system/src/components/__tests__/MainMenuItem-test.tsx
deleted file mode 100644 (file)
index 562613e..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-/* eslint-disable import/no-extraneous-dependencies */
-
-import { screen } from '@testing-library/react';
-import { render } from '../../helpers/testUtils';
-import { MainMenuItem } from '../MainMenuItem';
-
-it('should render default', () => {
-  render(
-    <MainMenuItem>
-      <a>Hi</a>
-    </MainMenuItem>
-  );
-
-  expect(screen.getByText('Hi')).toHaveStyle({
-    color: 'rgb(62, 67, 87)',
-    'border-bottom': '4px solid transparent',
-  });
-});
-
-it('should render active link', () => {
-  render(
-    <MainMenuItem>
-      <a className="active">Hi</a>
-    </MainMenuItem>
-  );
-
-  expect(screen.getByText('Hi')).toHaveStyle({
-    color: 'rgb(62, 67, 87)',
-    'border-bottom': '4px solid rgba(123,135,217,1)',
-  });
-});
-
-it('should render hovered link', () => {
-  render(
-    <MainMenuItem>
-      <a className="hover">Hi</a>
-    </MainMenuItem>
-  );
-
-  expect(screen.getByText('Hi')).toHaveStyle({
-    color: 'rgb(42, 47, 64)',
-    'border-bottom': '4px solid rgba(123,135,217,1)',
-  });
-});
diff --git a/server/sonar-web/design-system/src/components/__tests__/Menu-test.tsx b/server/sonar-web/design-system/src/components/__tests__/Menu-test.tsx
new file mode 100644 (file)
index 0000000..b53bce8
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* eslint-disable import/no-extraneous-dependencies */
+
+import { screen } from '@testing-library/react';
+import { render } from '../../helpers/testUtils';
+import { MainMenu } from '../MainMenu';
+
+it('should render MainMenu', () => {
+  render(<MainMenu>Children</MainMenu>);
+
+  expect(screen.getByText('Children')).toBeInTheDocument();
+});
diff --git a/server/sonar-web/design-system/src/components/__tests__/MenuItem-test.tsx b/server/sonar-web/design-system/src/components/__tests__/MenuItem-test.tsx
new file mode 100644 (file)
index 0000000..562613e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* eslint-disable import/no-extraneous-dependencies */
+
+import { screen } from '@testing-library/react';
+import { render } from '../../helpers/testUtils';
+import { MainMenuItem } from '../MainMenuItem';
+
+it('should render default', () => {
+  render(
+    <MainMenuItem>
+      <a>Hi</a>
+    </MainMenuItem>
+  );
+
+  expect(screen.getByText('Hi')).toHaveStyle({
+    color: 'rgb(62, 67, 87)',
+    'border-bottom': '4px solid transparent',
+  });
+});
+
+it('should render active link', () => {
+  render(
+    <MainMenuItem>
+      <a className="active">Hi</a>
+    </MainMenuItem>
+  );
+
+  expect(screen.getByText('Hi')).toHaveStyle({
+    color: 'rgb(62, 67, 87)',
+    'border-bottom': '4px solid rgba(123,135,217,1)',
+  });
+});
+
+it('should render hovered link', () => {
+  render(
+    <MainMenuItem>
+      <a className="hover">Hi</a>
+    </MainMenuItem>
+  );
+
+  expect(screen.getByText('Hi')).toHaveStyle({
+    color: 'rgb(42, 47, 64)',
+    'border-bottom': '4px solid rgba(123,135,217,1)',
+  });
+});
diff --git a/server/sonar-web/design-system/src/components/__tests__/NavBarTabs-test.tsx b/server/sonar-web/design-system/src/components/__tests__/NavBarTabs-test.tsx
new file mode 100644 (file)
index 0000000..0c2a64c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { screen } from '@testing-library/react';
+import { renderWithRouter } from '../../helpers/testUtils';
+import { FCProps } from '../../types/misc';
+import { DisabledTabLink, NavBarTabLink, NavBarTabs } from '../NavBarTabs';
+
+describe('NewNavBarTabs', () => {
+  it('should render correctly', () => {
+    setup();
+
+    expect(screen.getByRole('list')).toBeInTheDocument();
+    expect(screen.getByRole('listitem')).toBeInTheDocument();
+    expect(screen.getByRole('link')).toBeInTheDocument();
+    expect(screen.getByRole('link')).toHaveTextContent('test');
+  });
+
+  function setup() {
+    return renderWithRouter(
+      <NavBarTabs>
+        <NavBarTabLink text="test" to="/summary/new_code" />
+      </NavBarTabs>
+    );
+  }
+});
+
+describe('NewNavBarTabLink', () => {
+  it('should not be active when on different url', () => {
+    setupWithProps();
+
+    expect(screen.getByRole('link')).not.toHaveClass('active');
+  });
+
+  it('should be active when on same url', () => {
+    setupWithProps({ to: '/' });
+
+    expect(screen.getByRole('link')).toHaveClass('active');
+  });
+
+  it('should be active when active prop is set regardless of the url', () => {
+    setupWithProps({ active: true, withChevron: true });
+
+    expect(screen.getByRole('link')).toHaveClass('active');
+  });
+
+  it('should not be active when active prop is false regardless of the url', () => {
+    setupWithProps({ active: false, to: '/' });
+
+    expect(screen.getByRole('link')).not.toHaveClass('active');
+  });
+
+  function setupWithProps(props: Partial<FCProps<typeof NavBarTabLink>> = {}) {
+    return renderWithRouter(<NavBarTabLink text="test" to="/summary/new_code" {...props} />);
+  }
+});
+
+describe('DisabledTabLink', () => {
+  it('should render correctly', () => {
+    renderWithRouter(<DisabledTabLink label="label" overlay={<span>Overlay</span>} />);
+    expect(screen.getByRole('link')).toHaveClass('disabled-link');
+  });
+});
diff --git a/server/sonar-web/design-system/src/components/icons/ChevronDownIcon.tsx b/server/sonar-web/design-system/src/components/icons/ChevronDownIcon.tsx
new file mode 100644 (file)
index 0000000..863c092
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { useTheme } from '@emotion/react';
+import { themeColor } from '../../helpers/theme';
+import { CustomIcon, IconProps } from './Icon';
+
+export default function ChevronDownIcon({ fill = 'currentColor', ...iconProps }: IconProps) {
+  const theme = useTheme();
+  return (
+    <CustomIcon {...iconProps}>
+      <path
+        clipRule="evenodd"
+        d="M12.7236 5.83199c.1953.19527.1953.51185 0 .70711l-4.18499 4.185c-.19526.1953-.51184.1953-.7071 0l-4.18503-4.185c-.19527-.19526-.19527-.51184 0-.70711.19526-.19526.51184-.19526.7071 0l3.83148 3.83148 3.83144-3.83148c.1953-.19526.5119-.19526.7071 0Z"
+        fill={themeColor(fill)({ theme })}
+        fillRule="evenodd"
+      />
+    </CustomIcon>
+  );
+}
index 3b681fbe1b8e61b93a1dc457ef94a4288d2c82db..a8ba3597407d78235d2ee05516651671e3a3b1ef 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+export { default as ChevronDownIcon } from './ChevronDownIcon';
 export { default as ClockIcon } from './ClockIcon';
 export { FlagErrorIcon } from './FlagErrorIcon';
 export { FlagInfoIcon } from './FlagInfoIcon';
index 67b83f5d7afe45189fab7f246bbb2cb29bd0887e..4cb65f8730c7a2c27412fda8c139295f3f567a73 100644 (file)
@@ -32,7 +32,8 @@ export * from './InteractiveIcon';
 export { default as Link } from './Link';
 export * from './MainAppBar';
 export * from './MainMenu';
-export { MainMenuItem } from './MainMenuItem';
+export * from './MainMenuItem';
+export * from './NavBarTabs';
 export * from './popups';
 export * from './SonarQubeLogo';
 export * from './Text';
index 7a86822c0b4027b725f4f9c4e166204b3b7fbeb2..ea031787166ef8e4950cdb0df2a82c3973e1782f 100644 (file)
@@ -365,6 +365,7 @@ const lightTheme = {
     subnavigationBorder: COLORS.grey[100],
     subnavigationSeparator: COLORS.grey[50],
     subnavigationSubheading: COLORS.blueGrey[25],
+    subnavigationDisabled: COLORS.blueGrey[300],
 
     // footer
     footer: COLORS.white,
@@ -675,6 +676,7 @@ const lightTheme = {
   borders: {
     default: ['1px', 'solid', ...COLORS.grey[50]],
     active: ['4px', 'solid', ...primary.light],
+    xsActive: ['3px', 'solid', ...primary.light],
     focus: ['4px', 'solid', ...secondary.default, OPACITY_20_PERCENT],
   },
 
index 9e3da2c2c60058c058dc45817e2e66299926b11e..c23e466598920ed4a41ceb132361c9a4bbe0894e 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import classNames from 'classnames';
+import { DisabledTabLink, NavBarTabLink, NavBarTabs, Tooltip } from 'design-system';
 import * as React from 'react';
 import { NavLink } from 'react-router-dom';
 import { ButtonLink } from '../../../../components/controls/buttons';
 import Dropdown from '../../../../components/controls/Dropdown';
-import Tooltip from '../../../../components/controls/Tooltip';
 import BulletListIcon from '../../../../components/icons/BulletListIcon';
 import DropdownIcon from '../../../../components/icons/DropdownIcon';
-import NavBarTabs from '../../../../components/ui/NavBarTabs';
+import SQNavBarTabs from '../../../../components/ui/NavBarTabs';
 import { getBranchLikeQuery, isPullRequest } from '../../../../helpers/branch-like';
 import { hasMessage, translate, translateWithParameters } from '../../../../helpers/l10n';
 import { getPortfolioUrl, getProjectQueryUrl } from '../../../../helpers/urls';
@@ -148,7 +148,7 @@ export class Menu extends React.PureComponent<Props> {
     pathname,
     additionalQueryParams = {},
   }: {
-    label: React.ReactNode;
+    label: string;
     pathname: string;
     additionalQueryParams?: Dict<string>;
   }) => {
@@ -158,25 +158,16 @@ export class Menu extends React.PureComponent<Props> {
     if (isApplicationChildInaccessble) {
       return this.renderLinkWhenInaccessibleChild(label);
     }
-    return (
-      <li>
-        {hasAnalysis ? (
-          <NavLink
-            to={{
-              pathname,
-              search: new URLSearchParams({ ...query, ...additionalQueryParams }).toString(),
-            }}
-          >
-            {label}
-          </NavLink>
-        ) : (
-          <Tooltip overlay={translate('layout.must_be_configured')}>
-            <a aria-disabled="true" className="disabled-link">
-              {label}
-            </a>
-          </Tooltip>
-        )}
-      </li>
+    return hasAnalysis ? (
+      <NavBarTabLink
+        to={{
+          pathname,
+          search: new URLSearchParams({ ...query, ...additionalQueryParams }).toString(),
+        }}
+        text={label}
+      />
+    ) : (
+      <DisabledTabLink overlay={translate('layout.must_be_configured')} label={label} />
     );
   };
 
@@ -185,9 +176,7 @@ export class Menu extends React.PureComponent<Props> {
 
     if (this.isPortfolio()) {
       return this.isGovernanceEnabled() ? (
-        <li>
-          <NavLink to={getPortfolioUrl(id)}>{translate('overview.page')}</NavLink>
-        </li>
+        <NavBarTabLink to={getPortfolioUrl(id)} text={translate('overview.page')} />
       ) : null;
     }
 
@@ -196,9 +185,7 @@ export class Menu extends React.PureComponent<Props> {
       return this.renderLinkWhenInaccessibleChild(translate('overview.page'));
     }
     return (
-      <li>
-        <NavLink to={getProjectQueryUrl(id, branchLike)}>{translate('overview.page')}</NavLink>
-      </li>
+      <NavBarTabLink to={getProjectQueryUrl(id, branchLike)} text={translate('overview.page')} />
     );
   };
 
@@ -638,7 +625,7 @@ export class Menu extends React.PureComponent<Props> {
   render() {
     return (
       <div className="display-flex-center display-flex-space-between">
-        <NavBarTabs>
+        <NavBarTabs className="it__navbar-tabs">
           {this.renderDashboardLink()}
           {this.renderBreakdownLink()}
           {this.renderIssuesLink()}
@@ -649,10 +636,10 @@ export class Menu extends React.PureComponent<Props> {
           {this.renderActivityLink()}
           {this.renderExtensions()}
         </NavBarTabs>
-        <NavBarTabs>
+        <SQNavBarTabs>
           {this.renderAdministration()}
           {this.renderProjectInformationButton()}
-        </NavBarTabs>
+        </SQNavBarTabs>
       </div>
     );
   }
index c3c0fb61b63690abad44ee49c679cb552feb5d17..ad00e62a704fe335c15b08ffaeb95e967f54a271 100644 (file)
@@ -65,6 +65,29 @@ it('should render correctly', () => {
   expect(screen.getByRole('link', { name: 'ComponentBar' })).toBeInTheDocument();
 });
 
+it('should render correctly when on a Portofolio', () => {
+  const component = {
+    ...BASE_COMPONENT,
+    configuration: {
+      showSettings: true,
+      extensions: [
+        { key: 'foo', name: 'Foo' },
+        { key: 'bar', name: 'Bar' },
+      ],
+    },
+    qualifier: ComponentQualifier.Portfolio,
+    extensions: [
+      { key: 'governance/foo', name: 'governance foo' },
+      { key: 'governance/bar', name: 'governance bar' },
+    ],
+  };
+  renderMenu({ component });
+  expect(screen.getByRole('link', { name: 'overview.page' })).toBeInTheDocument();
+  expect(screen.getByRole('link', { name: 'issues.page' })).toBeInTheDocument();
+  expect(screen.getByRole('link', { name: 'layout.measures' })).toBeInTheDocument();
+  expect(screen.getByRole('link', { name: 'portfolio_breakdown.page' })).toBeInTheDocument();
+});
+
 it('should render correctly when on a branch', () => {
   renderMenu({
     branchLike: mockBranch(),
@@ -115,8 +138,8 @@ it('should disable links if no analysis has been done', () => {
     },
   });
   expect(screen.getByRole('link', { name: 'overview.page' })).toBeInTheDocument();
-  expect(screen.queryByRole('link', { name: 'issues.page' })).not.toBeInTheDocument();
-  expect(screen.queryByRole('link', { name: 'layout.measures' })).not.toBeInTheDocument();
+  expect(screen.queryByRole('link', { name: 'issues.page' })).toHaveClass('disabled-link');
+  expect(screen.queryByRole('link', { name: 'layout.measures' })).toHaveClass('disabled-link');
   expect(screen.getByRole('button', { name: 'project.info.title' })).toBeInTheDocument();
 });
 
index 80d92c8995e98c8204669285c78e0bb9b5a34102..325b7989ad4b002dff8dc16bcc120c5a74c962bc 100644 (file)
@@ -28,7 +28,7 @@ interface Props extends React.HTMLAttributes<HTMLUListElement> {
 
 export default function NavBarTabs({ children, className, ...other }: Props) {
   return (
-    <ul {...other} className={classNames('navbar-tabs', className)}>
+    <ul {...other} className={classNames('it__navbar-tabs navbar-tabs', className)}>
       {children}
     </ul>
   );