]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20023 CCT guide for issues list page
authorViktor Vorona <viktor.vorona@sonarsource.com>
Thu, 3 Aug 2023 13:59:01 +0000 (15:59 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Aug 2023 20:02:48 +0000 (20:02 +0000)
15 files changed:
server/sonar-web/design-system/package.json
server/sonar-web/design-system/src/components/Guide.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/buttons/Button.tsx
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/design-system/src/components/popups.tsx
server/sonar-web/package.json
server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
server/sonar-web/src/main/js/apps/issues/test-utils.tsx
server/sonar-web/src/main/js/types/users.ts
server/sonar-web/yarn.lock
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 52f053c905bf0635f75541196334b1695047f87f..5eced4921149b85873587c0c5fe28afc173bc41a 100644 (file)
@@ -73,6 +73,7 @@
     "react-helmet-async": "1.3.0",
     "react-highlight-words": "0.20.0",
     "react-intl": "6.4.4",
+    "react-joyride": "2.5.5",
     "react-modal": "3.16.1",
     "react-router-dom": "6.11.2",
     "react-select": "5.7.3",
diff --git a/server/sonar-web/design-system/src/components/Guide.tsx b/server/sonar-web/design-system/src/components/Guide.tsx
new file mode 100644 (file)
index 0000000..6aaaf15
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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 { Suspense } from 'react';
+import ReactJoyride, { Props as JoyrideProps, TooltipRenderProps } from 'react-joyride';
+import tw from 'twin.macro';
+import { PopupZLevel } from '../helpers';
+import { Spinner } from './DeferredSpinner';
+import { ButtonPrimary, ButtonSecondary, WrapperButton } from './buttons';
+import { CloseIcon } from './icons';
+import { PopupWrapper } from './popups';
+
+type Props = JoyrideProps;
+
+// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
+(window as any).global = (window as any).global ?? {};
+
+const Popup = styled(PopupWrapper)`
+  position: relative;
+  width: 300px;
+  border: none;
+  ${tw`sw-body-sm`}
+  ${tw`sw-p-3`}
+`;
+
+function TooltipComponent({
+  continuous,
+  index,
+  step,
+  size,
+  isLastStep,
+  backProps,
+  skipProps,
+  closeProps,
+  primaryProps,
+  tooltipProps,
+}: TooltipRenderProps) {
+  return (
+    <Popup zLevel={PopupZLevel.Absolute} {...tooltipProps}>
+      <div className="sw-flex sw-justify-between">
+        <b className="sw-mb-2">{step.title}</b>
+        <WrapperButton
+          className="sw-w-[30px] sw-h-[30px] sw--mt-2 sw--mr-2 sw-flex sw-justify-center"
+          {...skipProps}
+        >
+          <CloseIcon className="sw-mr-0" />
+        </WrapperButton>
+      </div>
+      <div>{step.content}</div>
+      <div className="sw-flex sw-justify-between sw-items-center sw-mt-3">
+        <b>
+          {index + 1} of {size}
+        </b>
+        <div>
+          {index > 0 && (
+            <ButtonSecondary className="sw-mr-2" {...backProps}>
+              {backProps.title}
+            </ButtonSecondary>
+          )}
+          {continuous && !isLastStep && (
+            <ButtonPrimary {...primaryProps}>{primaryProps.title}</ButtonPrimary>
+          )}
+          {(!continuous || isLastStep) && (
+            <ButtonPrimary {...closeProps}>{closeProps.title}</ButtonPrimary>
+          )}
+        </div>
+      </div>
+    </Popup>
+  );
+}
+
+export function Guide(props: Props) {
+  return (
+    <Suspense fallback={<Spinner />}>
+      <ReactJoyride
+        scrollDuration={0}
+        scrollOffset={250}
+        tooltipComponent={TooltipComponent}
+        {...props}
+      />
+    </Suspense>
+  );
+}
index 2d2ee37f744094a28506cdcbe67a98f032acdfca..bfcf1788f66c4721252fa0900eb0c681e1bd960d 100644 (file)
@@ -39,7 +39,7 @@ export interface ButtonProps extends AllowedButtonAttributes {
   icon?: React.ReactNode;
   innerRef?: React.Ref<HTMLButtonElement>;
   isExternal?: LinkProps['isExternal'];
-  onClick?: (event?: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => unknown;
+  onClick?: (event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => unknown;
 
   preventDefault?: boolean;
   reloadDocument?: LinkProps['reloadDocument'];
index 92aa59aca202fa13f993f72ee932f64e6b507d1c..b839c22726261650db9d2f67590a53b67f7a98d2 100644 (file)
@@ -43,6 +43,7 @@ export * from './FavoriteButton';
 export { FlagMessage } from './FlagMessage';
 export * from './FlowStep';
 export * from './GenericAvatar';
+export * from './Guide';
 export * from './HighlightedSection';
 export { Histogram } from './Histogram';
 export { HotspotRating } from './HotspotRating';
index a80d9b949ea6e3b63c25968953645a2f704d0860..d24f691777bf0d0c4f37801aad586cff4e47fae6 100644 (file)
@@ -200,7 +200,7 @@ export class Popup extends React.PureComponent<PopupProps, State> {
   }
 }
 
-const PopupWrapper = styled.div<{ zLevel: PopupZLevel }>`
+export const PopupWrapper = styled.div<{ zLevel: PopupZLevel }>`
   position: ${({ zLevel }) => (zLevel === PopupZLevel.Global ? 'fixed' : 'absolute')};
   background-color: ${themeColor('popup')};
   color: ${themeContrast('popup')};
index c7808145a3014c06b5867d5d5dda529649b5cf09..2540b44259505e07b05e0e317ecf37cdae253948 100644 (file)
@@ -36,6 +36,7 @@
     "react-helmet-async": "1.3.0",
     "react-highlight-words": "0.20.0",
     "react-intl": "6.4.4",
+    "react-joyride": "2.5.5",
     "react-modal": "3.16.1",
     "react-router-dom": "6.11.2",
     "react-select": "5.7.3",
diff --git a/server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts b/server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts
new file mode 100644 (file)
index 0000000..b5d4200
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+export default class LocalStorageMock {
+  store: Record<string, string>;
+
+  constructor() {
+    this.store = {};
+  }
+
+  clear() {
+    this.store = {};
+  }
+
+  getItem(key: string) {
+    return this.store[key] || null;
+  }
+
+  setItem(key: string, value: string) {
+    this.store[key] = String(value);
+  }
+
+  removeItem(key: string) {
+    delete this.store[key];
+  }
+
+  get length() {
+    return Object.keys(this.store).length;
+  }
+
+  key(index: number) {
+    return Object.keys(this.store)[index] || null;
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx
new file mode 100644 (file)
index 0000000..da88c12
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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 { Guide } from 'design-system';
+import React, { useEffect } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { dismissNotice } from '../../../api/users';
+import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
+import DocLink from '../../../components/common/DocLink';
+import { translate } from '../../../helpers/l10n';
+import { NoticeType } from '../../../types/users';
+
+interface Props {
+  run?: boolean;
+}
+
+export default function IssueListGuide({ run }: Props) {
+  const { currentUser } = React.useContext(CurrentUserContext);
+  const hasLocalStorageValue = localStorage.getItem(NoticeType.ISSUE_GUIDE) === 'true';
+
+  useEffect(() => {
+    if (!currentUser.dismissedNotices.IssueListGuiding && hasLocalStorageValue) {
+      dismissNotice(NoticeType.ISSUE_GUIDE);
+    }
+  }, [currentUser, hasLocalStorageValue]);
+
+  if (hasLocalStorageValue || currentUser.dismissedNotices.IssueListGuiding) {
+    return null;
+  }
+
+  const onToggle = (props: { action: string }) => {
+    if (props.action === 'reset') {
+      if (currentUser.isLoggedIn) {
+        dismissNotice(NoticeType.ISSUE_GUIDE);
+      } else {
+        localStorage.setItem(NoticeType.ISSUE_GUIDE, 'true');
+      }
+    }
+  };
+
+  const constructContent = (
+    first: string,
+    second: string,
+    extraContent?: string | React.ReactNode
+  ) => (
+    <>
+      <span>{translate(first)}</span>
+      <br />
+      <br />
+      <span>{translate(second)}</span>
+      {extraContent ?? null}
+    </>
+  );
+
+  const steps = [
+    {
+      target: '[data-guiding-id="issuelist-1"]',
+      content: constructContent('guiding.issue_list.1.content.1', 'guiding.issue_list.1.content.2'),
+      title: translate('guiding.issue_list.1.title'),
+      placement: 'right' as const,
+      disableBeacon: true,
+      floaterProps: {
+        disableAnimation: true,
+      },
+    },
+    {
+      target: '[data-guiding-id="issuelist-2"]',
+      content: constructContent('guiding.issue_list.2.content.1', 'guiding.issue_list.2.content.2'),
+      title: translate('guiding.issue_list.2.title'),
+      placement: 'right' as const,
+      disableBeacon: true,
+      floaterProps: {
+        disableAnimation: true,
+      },
+    },
+    {
+      target: '[data-guiding-id="issuelist-3"]',
+      content: constructContent('guiding.issue_list.2.content.1', 'guiding.issue_list.2.content.2'),
+      title: translate('guiding.issue_list.3.title'),
+      placement: 'right' as const,
+      disableBeacon: true,
+      floaterProps: {
+        disableAnimation: true,
+      },
+    },
+    {
+      target: '[data-guiding-id="issuelist-4"]',
+      content: constructContent(
+        'guiding.issue_list.4.content.1',
+        'guiding.issue_list.4.content.2',
+        <ul className="sw-mt-2 sw-pl-5 sw-list-disc">
+          <li>{translate('guiding.issue_list.4.content.list.1')}</li>
+          <li>{translate('guiding.issue_list.4.content.list.2')}</li>
+          <li>{translate('guiding.issue_list.4.content.list.3')}</li>
+        </ul>
+      ),
+      title: translate('guiding.issue_list.4.title'),
+      disableScrolling: true,
+      disableBeacon: true,
+      floaterProps: {
+        disableAnimation: true,
+      },
+    },
+    {
+      target: 'body',
+      content: (
+        <FormattedMessage
+          id="guiding.issue_list.5.content"
+          defaultMessage={translate('guiding.issue_list.5.content')}
+          values={{
+            link: (
+              <DocLink to="/user-guide/clean-code" className="sw-capitalize">
+                {translate('documentation')}
+              </DocLink>
+            ),
+          }}
+        />
+      ),
+      title: translate('guiding.issue_list.5.title'),
+      placement: 'center' as const,
+      disableBeacon: true,
+      floaterProps: {
+        disableAnimation: true,
+      },
+    },
+  ];
+
+  return <Guide callback={onToggle} steps={steps} run={run} continuous />;
+}
index f9bf57cbff19f051191ba6c269486738b6277128..1975ae716dab454519926e1ecef43dbda94daf44 100644 (file)
@@ -100,6 +100,7 @@ import {
   shouldOpenStandardsFacet,
 } from '../utils';
 import BulkChangeModal, { MAX_PAGE_SIZE } from './BulkChangeModal';
+import IssueListGuide from './IssueListGuide';
 import IssueReviewHistoryAndComments from './IssueReviewHistoryAndComments';
 import IssuesList from './IssuesList';
 import IssuesSourceViewer from './IssuesSourceViewer';
@@ -1303,6 +1304,8 @@ export class App extends React.PureComponent<Props, State> {
 
   render() {
     const { openIssue } = this.state;
+    const { component, location } = this.props;
+    const open = getOpen(location.query);
 
     return (
       <PageWrapperStyle id="issues-page">
@@ -1310,6 +1313,7 @@ export class App extends React.PureComponent<Props, State> {
           <PageContentFontWrapper className="sw-body-sm">
             <div className="sw-w-full sw-flex" id="issues-page">
               <Suggestions suggestions="issues" />
+              <IssueListGuide run={!open && !component?.needIssueSync} />
 
               {openIssue ? (
                 <Helmet
index 4797ecb36ad82eed4ade7ad1f3adefe4693711be..48ed584f7c6846563f23a556e15601952ba4df2a 100644 (file)
@@ -32,12 +32,14 @@ export function AttributeCategoryFacet(props: Props) {
   const { categories = [], ...rest } = props;
 
   return (
-    <SimpleListStyleFacet
-      property="cleanCodeAttributeCategory"
-      itemNamePrefix="issue.clean_code_attribute_category"
-      listItems={CATEGORIES}
-      selectedItems={categories}
-      {...rest}
-    />
+    <div data-guiding-id="issuelist-1">
+      <SimpleListStyleFacet
+        property="cleanCodeAttributeCategory"
+        itemNamePrefix="issue.clean_code_attribute_category"
+        listItems={CATEGORIES}
+        selectedItems={categories}
+        {...rest}
+      />
+    </div>
   );
 }
index 2851cc06a60b4879b3cc8d420bbc176c36475f0b..d0dec6151144e85bfd11975639880a8f91674ef5 100644 (file)
@@ -32,12 +32,14 @@ export function SoftwareQualityFacet(props: Props) {
   const { qualities = [], ...rest } = props;
 
   return (
-    <SimpleListStyleFacet
-      property="impactSoftwareQuality"
-      itemNamePrefix="issue.software_quality"
-      listItems={QUALITIES}
-      selectedItems={qualities}
-      {...rest}
-    />
+    <div data-guiding-id="issuelist-2">
+      <SimpleListStyleFacet
+        property="impactSoftwareQuality"
+        itemNamePrefix="issue.software_quality"
+        listItems={QUALITIES}
+        selectedItems={qualities}
+        {...rest}
+      />
+    </div>
   );
 }
index fc243e293f1153a6ed8326454aab162a1bdab696..42af36dee897a07eff18ad0640aeab8796bdcf5a 100644 (file)
@@ -127,6 +127,8 @@ export const ui = {
   ruleFacetList: byRole('list', { name: 'issues.facet.rules' }),
   ruleFacetSearch: byPlaceholderText('search.search_for_rules'),
   tagFacetSearch: byPlaceholderText('search.search_for_tags'),
+
+  guidePopup: byRole('alertdialog'),
 };
 
 export async function waitOnDataLoaded() {
index ab2421f700ec74f39c4f663f564229e79c6cdc69..d7acd57f448af05377a7776bd39a13250c812c43 100644 (file)
@@ -32,6 +32,7 @@ export interface Notice {
 export enum NoticeType {
   EDUCATION_PRINCIPLES = 'educationPrinciples',
   SONARLINT_AD = 'sonarlintAd',
+  ISSUE_GUIDE = 'issueCleanCodeGuide',
 }
 
 export interface LoggedInUser extends CurrentUser, UserActive {
index 50ecf6de2b400e0f6fb201056b62b3ec45603591..bd24a6320ebf1843d67430367426f1365e247507 100644 (file)
@@ -3074,6 +3074,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@gilbarbara/deep-equal@npm:^0.1.1":
+  version: 0.1.2
+  resolution: "@gilbarbara/deep-equal@npm:0.1.2"
+  checksum: 78d4e76d36cbee639c008a63be52c1ac803212ff2560e55f68d2b8b2a6ac5e746c1976854cf101483ca18a9911aed2349da147b7756be43e75efb95e3f24468b
+  languageName: node
+  linkType: hard
+
 "@humanwhocodes/config-array@npm:^0.11.10":
   version: 0.11.10
   resolution: "@humanwhocodes/config-array@npm:0.11.10"
@@ -4995,6 +5002,7 @@ __metadata:
     react-helmet-async: 1.3.0
     react-highlight-words: 0.20.0
     react-intl: 6.4.4
+    react-joyride: 2.5.5
     react-modal: 3.16.1
     react-router-dom: 6.11.2
     react-select: 5.7.3
@@ -6499,6 +6507,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"deepmerge@npm:^4.3.1":
+  version: 4.3.1
+  resolution: "deepmerge@npm:4.3.1"
+  checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052
+  languageName: node
+  linkType: hard
+
 "define-properties@npm:^1.1.2, define-properties@npm:^1.1.3":
   version: 1.1.3
   resolution: "define-properties@npm:1.1.3"
@@ -6608,6 +6623,7 @@ __metadata:
     react-helmet-async: 1.3.0
     react-highlight-words: 0.20.0
     react-intl: 6.4.4
+    react-joyride: 2.5.5
     react-modal: 3.16.1
     react-router-dom: 6.11.2
     react-select: 5.7.3
@@ -7707,7 +7723,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"exenv@npm:^1.2.0":
+"exenv@npm:^1.2.0, exenv@npm:^1.2.2":
   version: 1.2.2
   resolution: "exenv@npm:1.2.2"
   checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc
@@ -8875,6 +8891,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-lite@npm:^0.8.2":
+  version: 0.8.2
+  resolution: "is-lite@npm:0.8.2"
+  checksum: 0ee62cb238c2a044f58d1cd139fb0b48026c407ec8625ee6572b417f164e17ec937f0a0785f466e320749a796c316a3b78dcb4b520f7ddd4b9de38ad5a23d70f
+  languageName: node
+  linkType: hard
+
+"is-lite@npm:^0.9.2":
+  version: 0.9.2
+  resolution: "is-lite@npm:0.9.2"
+  checksum: 8c4d2c58cf99a8289715925c0c3175dadf63e5ad293ad395ce650430ce90afe533a84ad0ffdeed0ce277dabdae63acdd3dac5d9b629bd61c3c0c620e2376f26e
+  languageName: node
+  linkType: hard
+
 "is-map@npm:^2.0.1, is-map@npm:^2.0.2":
   version: 2.0.2
   resolution: "is-map@npm:2.0.2"
@@ -11050,6 +11080,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"popper.js@npm:^1.16.0":
+  version: 1.16.1
+  resolution: "popper.js@npm:1.16.1"
+  checksum: c56ae5001ec50a77ee297a8061a0221d99d25c7348d2e6bcd3e45a0d0f32a1fd81bca29d46cb0d4bdf13efb77685bd6a0ce93f9eb3c608311a461f945fffedbe
+  languageName: node
+  linkType: hard
+
 "postcss-calc@npm:9.0.1":
   version: 9.0.1
   resolution: "postcss-calc@npm:9.0.1"
@@ -11388,6 +11425,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-floater@npm:^0.7.6":
+  version: 0.7.6
+  resolution: "react-floater@npm:0.7.6"
+  dependencies:
+    deepmerge: ^4.2.2
+    exenv: ^1.2.2
+    is-lite: ^0.8.2
+    popper.js: ^1.16.0
+    prop-types: ^15.8.1
+    react-proptype-conditional-require: ^1.0.4
+    tree-changes: ^0.9.1
+  peerDependencies:
+    react: 15 - 18
+    react-dom: 15 - 18
+  checksum: 8268e14fbdf9393b39300f3c90ea2de382782f1d959176579e30841095a73f9240ec05fce3ec89e8b4e58cbc46c9b043b2ad0b338f5c5d3b91168b16a4282ac1
+  languageName: node
+  linkType: hard
+
 "react-helmet-async@npm:1.3.0":
   version: 1.3.0
   resolution: "react-helmet-async@npm:1.3.0"
@@ -11476,6 +11531,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-joyride@npm:2.5.5":
+  version: 2.5.5
+  resolution: "react-joyride@npm:2.5.5"
+  dependencies:
+    deepmerge: ^4.3.1
+    exenv: ^1.2.2
+    is-lite: ^0.9.2
+    prop-types: ^15.8.1
+    react-floater: ^0.7.6
+    react-is: ^16.13.1
+    scroll: ^3.0.1
+    scrollparent: ^2.1.0
+    tree-changes: ^0.9.2
+  peerDependencies:
+    react: 15 - 18
+    react-dom: 15 - 18
+  checksum: ca4a18631e12638e10f7ebb063880f66a984a4edc0fe81f87649ac1ec32c3790a03c6cbe10f189c029d64af52eef4173042c676024987accb0aeaa4e1216bb04
+  languageName: node
+  linkType: hard
+
 "react-lifecycles-compat@npm:^3.0.0, react-lifecycles-compat@npm:^3.0.4":
   version: 3.0.4
   resolution: "react-lifecycles-compat@npm:3.0.4"
@@ -11498,6 +11573,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-proptype-conditional-require@npm:^1.0.4":
+  version: 1.0.4
+  resolution: "react-proptype-conditional-require@npm:1.0.4"
+  checksum: 78f82d15b2c77c14fd8fbcbbed279850df3a856984aacd519ee6c2162e034b114b8ac47c00157b84ef7c98c0711d933a0177d9d54555629cf381f54341bb0e8f
+  languageName: node
+  linkType: hard
+
 "react-refresh@npm:^0.14.0":
   version: 0.14.0
   resolution: "react-refresh@npm:0.14.0"
@@ -12051,6 +12133,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"scroll@npm:^3.0.1":
+  version: 3.0.1
+  resolution: "scroll@npm:3.0.1"
+  checksum: e6b045347adace30035073882e6ef2af7e1c81dd611faf3a578ca8cd0d1a3a9da54932dd97ed6fd99c9573351c758fa50e6d8ed4afb5bd3a33794b6c48d25922
+  languageName: node
+  linkType: hard
+
+"scrollparent@npm:^2.1.0":
+  version: 2.1.0
+  resolution: "scrollparent@npm:2.1.0"
+  checksum: 646cfdaf981f94b52f1020cc26b18ff1a751c2fa4e40777f6dfac314500d365b4db3fec39fd06be7b683f0a1dbd29df8ce1629db50381e9072433465b2f446fa
+  languageName: node
+  linkType: hard
+
 "select@npm:^1.1.2":
   version: 1.1.2
   resolution: "select@npm:1.1.2"
@@ -12798,6 +12894,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tree-changes@npm:^0.9.1, tree-changes@npm:^0.9.2":
+  version: 0.9.3
+  resolution: "tree-changes@npm:0.9.3"
+  dependencies:
+    "@gilbarbara/deep-equal": ^0.1.1
+    is-lite: ^0.8.2
+  checksum: 86d890b18e83f2a20e7257982aec62efa186abbb08de4cead1c8062c50793f5b3c5fd09f98d9f4b8784b921e830687c0ea9bdb42ef4abb2eb9d6782d8c56a673
+  languageName: node
+  linkType: hard
+
 "ts-interface-checker@npm:^0.1.9":
   version: 0.1.13
   resolution: "ts-interface-checker@npm:0.1.13"
index 0e7e5ffa12006b4bd42413a324cefca9039534b6..c5a3b5ab7dfc94a491c73aee0f972bd9d7056d27 100644 (file)
@@ -4951,3 +4951,29 @@ component_report.unsubscribe.description=If you no longer wish to receive these
 component_report.subscribe_x_success=Subscription successful. You will receive a {0} report for this {1} by email.
 component_report.unsubscribe_x_success=Subscription successfully canceled. You won't receive a {0} report for this {1} by email.
 component_report.unsubscribe_success=Subscription successfully canceled. You won't receive these reports by email anymore.
+
+
+#------------------------------------------------------------------------------
+#
+# GUIDING
+#
+#------------------------------------------------------------------------------
+guiding.issue_list.1.title=Introducing Clean Code attributes
+guiding.issue_list.1.content.1=Clean Code attributes are the characteristic that your code must have to be considered Clean Code.
+guiding.issue_list.1.content.2=You can now filter by these attributes to evaluate why your code is breaking away from being clean.
+guiding.issue_list.2.title=Introducing Software Qualities
+guiding.issue_list.2.content.1=A software quality is a characteristic of software that contributes to its lasting value.
+guiding.issue_list.2.content.2=You can now filter by these qualities to evaluate the areas in your software that are impacted by the introduction of code that isn't clean.
+guiding.issue_list.3.title=Severity and Software Qualities
+guiding.issue_list.3.content.1=Severities are now directly tied to the software quality impacted. This means that one software quality impacted has one severity.
+guiding.issue_list.3.content.2=There are three only 3 levels: high, medium, and low.
+guiding.issue_list.4.title=Type and old severity deprecated
+guiding.issue_list.4.content.1=Issue types and the old severities are deprecated and can no longer be modified.
+guiding.issue_list.4.content.2=You can now filter issues by:
+guiding.issue_list.4.content.list.1=Clean Code attributes
+guiding.issue_list.4.content.list.2=Software qualities
+guiding.issue_list.4.content.list.3=The severity of the software quality
+guiding.issue_list.5.title=Learn more
+guiding.issue_list.5.content=You can learn more about the approach to Clean Code in the {link}
+
+