]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19171 New Execution Flow component
authorJeremy Davis <jeremy.davis@sonarsource.com>
Fri, 12 May 2023 15:27:56 +0000 (17:27 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 16 May 2023 20:02:50 +0000 (20:02 +0000)
server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/FlowStep.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/LocationMarker.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/ExecutionFlowAccordion-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/FlowStep-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/icons/IssueLocationIcon.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

diff --git a/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx b/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx
new file mode 100644 (file)
index 0000000..73ca6d4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 { ReactNode } from 'react';
+import tw from 'twin.macro';
+import { themeBorder, themeColor, themeContrast } from '../helpers/theme';
+import { BareButton } from './buttons';
+import { OpenCloseIndicator } from './icons/OpenCloseIndicator';
+
+interface Props {
+  children: ReactNode;
+  expanded?: boolean;
+  header: ReactNode;
+  id: string;
+  innerRef?: (node: HTMLDivElement) => void;
+  onClick?: () => void;
+}
+
+export function ExecutionFlowAccordion(props: Props) {
+  const { children, expanded, header, id, innerRef, onClick } = props;
+
+  return (
+    <Accordion className={classNames({ expanded })} ref={innerRef}>
+      <Expander
+        aria-controls={`${id}-flow-accordion`}
+        aria-expanded={expanded}
+        id={`${id}-flow-accordion-button`}
+        onClick={onClick}
+      >
+        {header}
+        <OpenCloseIndicator open={Boolean(expanded)} />
+      </Expander>
+
+      {expanded && <Body id={`${id}-flow-accordion-body`}>{children}</Body>}
+    </Accordion>
+  );
+}
+
+const Expander = styled(BareButton)`
+  ${tw`sw-flex sw-items-center sw-justify-between`}
+  ${tw`sw-box-border`}
+  ${tw`sw-p-2`}
+  ${tw`sw-w-full`}
+  ${tw`sw-cursor-pointer`}
+
+  color: ${themeContrast('subnavigationExecutionFlow')};
+  background-color: ${themeColor('subnavigationExecutionFlow')};
+`;
+
+const Accordion = styled.div`
+  ${tw`sw-flex sw-flex-col`}
+  ${tw`sw-rounded-1/2`}
+
+  border: ${themeBorder('default', 'subnavigationExecutionFlowBorder')};
+
+  &:hover {
+    border: ${themeBorder('default', 'subnavigationExecutionFlowActive')};
+  }
+
+  &.expanded {
+    border: ${themeBorder('default', 'subnavigationExecutionFlowActive')};
+    outline: ${themeBorder('focus', 'primary')};
+
+    ${Expander} {
+      border-bottom: ${themeBorder('default', 'subnavigationExecutionFlowBorder')};
+    }
+  }
+`;
+
+const Body = styled.div`
+  ${tw`sw-p-2`}
+
+  background-color: ${themeColor('subnavigationExecutionFlow')};
+`;
+
+ExecutionFlowAccordion.displayName = 'ExecutionFlowAccordion';
diff --git a/server/sonar-web/design-system/src/components/FlowStep.tsx b/server/sonar-web/design-system/src/components/FlowStep.tsx
new file mode 100644 (file)
index 0000000..13f0ccf
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 tw from 'twin.macro';
+import { themeColor } from '../helpers/theme';
+import { BaseLink } from './Link';
+import { LocationMarker, StyledMarker } from './LocationMarker';
+
+interface Props {
+  additionalMarkers?: React.ReactNode;
+  className?: string;
+  message?: string;
+  onClick?: () => void;
+  selected: boolean;
+  step?: number;
+}
+
+export function FlowStep(props: Props) {
+  const { additionalMarkers, className, message, selected, step } = props;
+
+  return (
+    <StyledLink className={className} onClick={props.onClick} to={{}}>
+      <>
+        <LocationMarker selected={selected} text={step} />
+        {additionalMarkers}
+      </>
+      <span>{message}</span>
+    </StyledLink>
+  );
+}
+
+const StyledLink = styled(BaseLink)`
+  ${tw`sw-p-1 sw-rounded-1/2`}
+  ${tw`sw-flex sw-items-center sw-flex-wrap sw-gap-2`}
+  ${tw`sw-body-sm`}
+  
+  color: ${themeColor('pageContent')};
+  border-bottom: none;
+
+  &.selected,
+  &:hover {
+    background-color: ${themeColor('codeLineLocationSelected')};
+  }
+
+  &:hover ${StyledMarker} {
+    background-color: ${themeColor('codeLineLocationMarkerSelected')};
+  }
+`;
diff --git a/server/sonar-web/design-system/src/components/LocationMarker.tsx b/server/sonar-web/design-system/src/components/LocationMarker.tsx
new file mode 100644 (file)
index 0000000..1ce6114
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 { forwardRef, LegacyRef } from 'react';
+import tw from 'twin.macro';
+import { themeColor, themeContrast } from '../helpers/theme';
+import { isDefined } from '../helpers/types';
+import { IssueLocationIcon } from './icons/IssueLocationIcon';
+
+interface Props {
+  className?: string;
+  onClick?: () => void;
+  selected: boolean;
+  text?: number | string;
+}
+
+function InternalLocationMarker(
+  { className, onClick, text, selected }: Props,
+  ref: LegacyRef<HTMLDivElement>
+) {
+  return (
+    <StyledMarker
+      className={classNames(className, {
+        selected,
+        concealed: !isDefined(text),
+        'sw-cursor-pointer': isDefined(onClick),
+      })}
+      onClick={onClick}
+      ref={ref}
+    >
+      {isDefined(text) ? text : <IssueLocationIcon />}
+    </StyledMarker>
+  );
+}
+
+export const LocationMarker = forwardRef<HTMLDivElement, Props>(InternalLocationMarker);
+
+export const StyledMarker = styled.div`
+  ${tw`sw-flex sw-grow-0 sw-items-center sw-justify-center`}
+  ${tw`sw-body-sm-highlight`}
+  ${tw`sw-rounded-1/2`}
+
+  height: 1.125rem;
+  color: ${themeContrast('codeLineLocationMarker')};
+  background-color: ${themeColor('codeLineLocationMarker')};
+
+  &.selected,
+  &:hover {
+    background-color: ${themeColor('codeLineLocationMarkerSelected')};
+  }
+
+  &:not(.concealed) {
+    ${tw`sw-px-1`}
+    ${tw`sw-self-start`}
+  }
+`;
diff --git a/server/sonar-web/design-system/src/components/__tests__/ExecutionFlowAccordion-test.tsx b/server/sonar-web/design-system/src/components/__tests__/ExecutionFlowAccordion-test.tsx
new file mode 100644 (file)
index 0000000..e17f074
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { FCProps } from '../../types/misc';
+import { ExecutionFlowAccordion } from '../ExecutionFlowAccordion';
+
+it('should render correctly', () => {
+  renderExecutionFlowAccordion({}, <div>flow-accordion-children</div>);
+  expect(screen.queryByText('flow-accordion-children')).not.toBeInTheDocument();
+});
+
+it('should render correctly expanded', () => {
+  renderExecutionFlowAccordion({ expanded: true }, <div>flow-accordion-children</div>);
+  expect(screen.getByText('flow-accordion-children')).toBeVisible();
+});
+
+function renderExecutionFlowAccordion(
+  props: Partial<FCProps<typeof ExecutionFlowAccordion>> = {},
+  children?: React.ReactNode
+) {
+  return render(
+    <ExecutionFlowAccordion header="header" id="id" {...props}>
+      {children}
+    </ExecutionFlowAccordion>
+  );
+}
diff --git a/server/sonar-web/design-system/src/components/__tests__/FlowStep-test.tsx b/server/sonar-web/design-system/src/components/__tests__/FlowStep-test.tsx
new file mode 100644 (file)
index 0000000..a69ad0e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 { FlowStep } from '../FlowStep';
+
+it('should render correctly', () => {
+  renderFlowStep({ step: 3, additionalMarkers: <span>additional</span> });
+
+  expect(screen.getByText('3')).toBeInTheDocument();
+  expect(screen.getByText('additional')).toBeInTheDocument();
+});
+
+function renderFlowStep(props: Partial<FCProps<typeof FlowStep>> = {}) {
+  return renderWithRouter(<FlowStep message="" selected={false} {...props} />);
+}
diff --git a/server/sonar-web/design-system/src/components/icons/IssueLocationIcon.tsx b/server/sonar-web/design-system/src/components/icons/IssueLocationIcon.tsx
new file mode 100644 (file)
index 0000000..9cf54fb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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 function IssueLocationIcon({ fill = 'currentColor', ...iconProps }: IconProps) {
+  const theme = useTheme();
+  const fillColor = themeColor(fill)({ theme });
+
+  return (
+    <CustomIcon {...iconProps}>
+      <path
+        clipRule="evenodd"
+        d="M8 11C8.79565 11 9.55871 10.6839 10.1213 10.1213C10.6839 9.55871 11 8.79565 11 8C11 7.20435 10.6839 6.44129 10.1213 5.87868C9.55871 5.31607 8.79565 5 8 5C7.20435 5 6.44129 5.31607 5.87868 5.87868C5.31607 6.44129 5 7.20435 5 8C5 8.79565 5.31607 9.55871 5.87868 10.1213C6.44129 10.6839 7.20435 11 8 11ZM8 7C7.73478 7 7.48043 7.10536 7.29289 7.29289C7.10536 7.48043 7 7.73478 7 8C7 8.26522 7.10536 8.51957 7.29289 8.70711C7.48043 8.89464 7.73478 9 8 9C8.26522 9 8.51957 8.89464 8.70711 8.70711C8.89464 8.51957 9 8.26522 9 8C9 7.73478 8.89464 7.48043 8.70711 7.29289C8.51957 7.10536 8.26522 7 8 7Z"
+        fill={fillColor}
+        fillRule="evenodd"
+      />
+    </CustomIcon>
+  );
+}
index c81fbbf5fbb6f24f7c5425ce9e3ef86d548bc213..ce7c413ff6342d14ff18ee6b6ef49fe800cf17f3 100644 (file)
@@ -38,6 +38,7 @@ export { FlagWarningIcon } from './FlagWarningIcon';
 export { HelperHintIcon } from './HelperHintIcon';
 export { HomeFillIcon } from './HomeFillIcon';
 export { HomeIcon } from './HomeIcon';
+export { IssueLocationIcon } from './IssueLocationIcon';
 export { LinkIcon } from './LinkIcon';
 export { LockIcon } from './LockIcon';
 export { MainBranchIcon } from './MainBranchIcon';
index 298efab3dd0abf4b03109a372d6d06b0dbfe3b5c..74092bf20ace271f0f0494f4fab5b7f896029cd6 100644 (file)
@@ -32,10 +32,12 @@ export { Dropdown } from './Dropdown';
 export * from './DropdownMenu';
 export { DropdownToggler } from './DropdownToggler';
 export * from './DuplicationsIndicator';
+export * from './ExecutionFlowAccordion';
 export * from './FacetBox';
 export * from './FacetItem';
 export { FailedQGConditionLink } from './FailedQGConditionLink';
 export { FlagMessage } from './FlagMessage';
+export * from './FlowStep';
 export * from './GenericAvatar';
 export * from './HighlightedSection';
 export { HotspotRating } from './HotspotRating';
@@ -46,6 +48,7 @@ export * from './InteractiveIcon';
 export * from './KeyboardHint';
 export * from './Link';
 export { StandoutLink as Link } from './Link';
+export * from './LocationMarker';
 export * from './MainAppBar';
 export * from './MainMenu';
 export * from './MainMenuItem';
index 24cd71f3ea33d7c816b20ab36d7888066e4e66b1..cbcebc1e18504a00e121d43d780a2897c543f97f 100644 (file)
@@ -169,6 +169,9 @@ export const lightTheme = {
 
     // code viewer
     codeLineIssueIndicator: COLORS.blueGrey[400], // Should be blueGrey[300], to be changed once code viewer is reworked
+    codeLineLocationMarker: COLORS.red[200],
+    codeLineLocationMarkerSelected: danger.lighter,
+    codeLineLocationSelected: COLORS.blueGrey[100],
 
     // checkbox
     checkboxHover: COLORS.indigo[50],
@@ -368,6 +371,9 @@ export const lightTheme = {
     subnavigationSeparator: COLORS.grey[50],
     subnavigationSubheading: COLORS.blueGrey[25],
     subnavigationDisabled: COLORS.blueGrey[300],
+    subnavigationExecutionFlow: COLORS.blueGrey[25],
+    subnavigationExecutionFlowBorder: secondary.default,
+    subnavigationExecutionFlowActive: COLORS.indigo[500],
 
     // footer
     footer: COLORS.white,
@@ -526,6 +532,10 @@ export const lightTheme = {
     toggle: secondary.darker,
     toggleHover: secondary.darker,
 
+    // code viewer
+    codeLineLocationMarker: COLORS.red[900],
+    codeLineLocationMarkerSelected: COLORS.red[900],
+
     // code snippet
     codeSnippetHighlight: danger.default,
 
@@ -625,6 +635,7 @@ export const lightTheme = {
 
     // subnavigation sidebar
     subnavigation: secondary.darker,
+    subnavigationExecutionFlow: COLORS.blueGrey[700],
     subnavigationHover: COLORS.blueGrey[700],
     subnavigationSubheading: secondary.dark,