]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15257 Adding aria attributes to code page
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Tue, 26 Apr 2022 12:05:19 +0000 (14:05 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 29 Apr 2022 20:03:19 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/code/__tests__/utils-test.tsx
server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx
server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentName-test.tsx
server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentName-test.tsx.snap
server/sonar-web/src/main/js/apps/code/utils.ts
server/sonar-web/src/main/js/components/icons/Icon.tsx
server/sonar-web/src/main/js/components/icons/QualifierIcon.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 4e73d7db90f518511b9d71d51d2804a3418a9dc3..88c9cbe9555403b5c481165aba9cc812c61c2153 100644 (file)
@@ -28,6 +28,7 @@ import {
 import {
   getCodeMetrics,
   loadMoreChildren,
+  mostCommonPrefix,
   retrieveComponent,
   retrieveComponentChildren
 } from '../utils';
@@ -142,3 +143,11 @@ describe('loadMoreChildren', () => {
     expect(getComponentBreadcrumbs).toHaveBeenCalledWith('key');
   });
 });
+
+describe('#mostCommonPrefix', () => {
+  it('should correctly find the common path prefix', () => {
+    expect(mostCommonPrefix(['src/main/ts/tests', 'src/main/java/tests'])).toEqual('src/main/');
+    expect(mostCommonPrefix(['src/main/ts/app', 'src/main/ts/app'])).toEqual('src/main/ts/');
+    expect(mostCommonPrefix(['src/main/ts', 'lib/main/ts'])).toEqual('');
+  });
+});
index 4f0b760f18468ca094a42d79a29e1791d97d0365..c555d2e1366d83850d4724547b91de15fb73d1e7 100644 (file)
@@ -33,6 +33,7 @@ import {
   isProject
 } from '../../../types/component';
 import { ComponentMeasure } from '../../../types/types';
+import { mostCommonPrefix } from '../utils';
 
 export function getTooltip(component: ComponentMeasure) {
   const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
@@ -44,21 +45,6 @@ export function getTooltip(component: ComponentMeasure) {
   return [component.name, component.key, component.branch].filter(s => !!s).join('\n\n');
 }
 
-export function mostCommonPrefix(strings: string[]) {
-  const sortedStrings = strings.slice(0).sort();
-  const firstString = sortedStrings[0];
-  const firstStringLength = firstString.length;
-  const lastString = sortedStrings[sortedStrings.length - 1];
-  let i = 0;
-  while (i < firstStringLength && firstString.charAt(i) === lastString.charAt(i)) {
-    i++;
-  }
-  const prefix = firstString.substr(0, i);
-  const prefixTokens = prefix.split(/[\s\\/]/);
-  const lastPrefixPart = prefixTokens[prefixTokens.length - 1];
-  return prefix.substr(0, prefix.length - lastPrefixPart.length);
-}
-
 export interface Props {
   branchLike?: BranchLike;
   canBrowse?: boolean;
@@ -76,21 +62,58 @@ export default function ComponentName({
   previous,
   canBrowse = false
 }: Props) {
-  const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR';
-  const prefix =
-    areBothDirs && previous !== undefined
-      ? mostCommonPrefix([component.name + '/', previous.name + '/'])
-      : '';
-  const name = prefix ? (
-    <span>
-      <span style={{ color: colors.secondFontColor }}>{prefix}</span>
-      <span>{component.name.substr(prefix.length)}</span>
+  const ariaLabel = unclickable ? translate('code.parent_folder') : undefined;
+
+  if (
+    [ComponentQualifier.Application, ComponentQualifier.Portfolio].includes(
+      rootComponent.qualifier as ComponentQualifier
+    ) &&
+    [ComponentQualifier.Application, ComponentQualifier.Project].includes(
+      component.qualifier as ComponentQualifier
+    )
+  ) {
+    return (
+      <span className="max-width-100 display-inline-flex-center">
+        <span className="text-ellipsis" title={getTooltip(component)} aria-label={ariaLabel}>
+          {renderNameWithIcon(
+            branchLike,
+            component,
+            previous,
+            rootComponent,
+            unclickable,
+            canBrowse
+          )}
+        </span>
+        {component.branch ? (
+          <span className="text-ellipsis spacer-left">
+            <BranchIcon className="little-spacer-right" />
+            <span className="note">{component.branch}</span>
+          </span>
+        ) : (
+          <span className="spacer-left badge flex-1">{translate('branches.main_branch')}</span>
+        )}
+      </span>
+    );
+  }
+  return (
+    <span
+      className="max-width-100 display-inline-block text-ellipsis"
+      title={getTooltip(component)}
+      aria-label={ariaLabel}>
+      {renderNameWithIcon(branchLike, component, previous, rootComponent, unclickable, canBrowse)}
     </span>
-  ) : (
-    component.name
   );
+}
 
-  let inner = null;
+function renderNameWithIcon(
+  branchLike: BranchLike | undefined,
+  component: ComponentMeasure,
+  previous: ComponentMeasure | undefined,
+  rootComponent: ComponentMeasure,
+  unclickable = false,
+  canBrowse = false
+) {
+  const name = renderName(component, previous);
 
   if (
     !unclickable &&
@@ -103,7 +126,7 @@ export default function ComponentName({
     )
       ? component.branch
       : undefined;
-    inner = (
+    return (
       <Link
         className="link-with-icon"
         to={getComponentOverviewUrl(component.refKey || component.key, component.qualifier, {
@@ -117,49 +140,31 @@ export default function ComponentName({
     if (component.key !== rootComponent.key) {
       Object.assign(query, { selected: component.key });
     }
-    inner = (
+    return (
       <Link className="link-with-icon" to={{ pathname: '/code', query }}>
         <QualifierIcon qualifier={component.qualifier} /> <span>{name}</span>
       </Link>
     );
-  } else {
-    inner = (
-      <span>
-        <QualifierIcon qualifier={component.qualifier} /> {name}
-      </span>
-    );
   }
+  return (
+    <span>
+      <QualifierIcon qualifier={component.qualifier} /> {name}
+    </span>
+  );
+}
 
-  if (
-    [ComponentQualifier.Application, ComponentQualifier.Portfolio].includes(
-      rootComponent.qualifier as ComponentQualifier
-    ) &&
-    [ComponentQualifier.Application, ComponentQualifier.Project].includes(
-      component.qualifier as ComponentQualifier
-    )
-  ) {
-    return (
-      <span className="max-width-100 display-inline-flex-center">
-        <span className="text-ellipsis" title={getTooltip(component)}>
-          {inner}
-        </span>
-        {component.branch ? (
-          <span className="text-ellipsis spacer-left">
-            <BranchIcon className="little-spacer-right" />
-            <span className="note">{component.branch}</span>
-          </span>
-        ) : (
-          <span className="spacer-left badge flex-1">{translate('branches.main_branch')}</span>
-        )}
-      </span>
-    );
-  } else {
-    return (
-      <span
-        className="max-width-100 display-inline-block text-ellipsis"
-        title={getTooltip(component)}>
-        {inner}
-      </span>
-    );
-  }
+function renderName(component: ComponentMeasure, previous: ComponentMeasure | undefined) {
+  const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR';
+  const prefix =
+    areBothDirs && previous !== undefined
+      ? mostCommonPrefix([component.name + '/', previous.name + '/'])
+      : '';
+  return prefix ? (
+    <span>
+      <span style={{ color: colors.secondFontColor }}>{prefix}</span>
+      <span>{component.name.slice(prefix.length)}</span>
+    </span>
+  ) : (
+    component.name
+  );
 }
index 49831b96a969386938e74722d8664c6ee2e568fc..7d0d3c0ddb9c61b7300631592be7b9a4fc40cffd 100644 (file)
@@ -22,7 +22,7 @@ import * as React from 'react';
 import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
 import { mockComponentMeasure } from '../../../../helpers/mocks/component';
 import { ComponentQualifier } from '../../../../types/component';
-import ComponentName, { getTooltip, mostCommonPrefix, Props } from '../ComponentName';
+import ComponentName, { getTooltip, Props } from '../ComponentName';
 
 describe('#getTooltip', () => {
   it('should correctly format component information', () => {
@@ -33,14 +33,6 @@ describe('#getTooltip', () => {
   });
 });
 
-describe('#mostCommonPrefix', () => {
-  it('should correctly find the common path prefix', () => {
-    expect(mostCommonPrefix(['src/main/ts/tests', 'src/main/java/tests'])).toEqual('src/main/');
-    expect(mostCommonPrefix(['src/main/ts/app', 'src/main/ts/app'])).toEqual('src/main/ts/');
-    expect(mostCommonPrefix(['src/main/ts', 'lib/main/ts'])).toEqual('');
-  });
-});
-
 describe('#ComponentName', () => {
   it('should render correctly for files', () => {
     expect(shallowRender()).toMatchSnapshot();
index d045ac053878665e43426c3f6d529c899b118c41..1b2109af89b0a3edc702453c65e4a6552fbf247c 100644 (file)
@@ -2,6 +2,7 @@
 
 exports[`#ComponentName should render breadcrumb correctly for APP 1`] = `
 <span
+  aria-label="code.parent_folder"
   className="max-width-100 display-inline-block text-ellipsis"
   title="Foo
 
@@ -19,6 +20,7 @@ foo"
 
 exports[`#ComponentName should render breadcrumb correctly for SVW 1`] = `
 <span
+  aria-label="code.parent_folder"
   className="max-width-100 display-inline-block text-ellipsis"
   title="Foo
 
@@ -36,6 +38,7 @@ foo"
 
 exports[`#ComponentName should render breadcrumb correctly for TRK 1`] = `
 <span
+  aria-label="code.parent_folder"
   className="max-width-100 display-inline-block text-ellipsis"
   title="Foo
 
@@ -53,6 +56,7 @@ foo"
 
 exports[`#ComponentName should render breadcrumb correctly for TRK 2`] = `
 <span
+  aria-label="code.parent_folder"
   className="max-width-100 display-inline-block text-ellipsis"
   title="Foo
 
@@ -70,6 +74,7 @@ foo"
 
 exports[`#ComponentName should render breadcrumb correctly for VW 1`] = `
 <span
+  aria-label="code.parent_folder"
   className="max-width-100 display-inline-block text-ellipsis"
   title="Foo
 
index 9dca10800ded15424f4a1fc715b3df5672c9ba49..9476ce4f0465cc8d1e1b94a39647decb5efd58f2 100644 (file)
@@ -271,3 +271,18 @@ export function loadMoreChildren(
       return r;
     });
 }
+
+export function mostCommonPrefix(strings: string[]) {
+  const sortedStrings = strings.slice(0).sort();
+  const firstString = sortedStrings[0];
+  const firstStringLength = firstString.length;
+  const lastString = sortedStrings[sortedStrings.length - 1];
+  let i = 0;
+  while (i < firstStringLength && firstString.charAt(i) === lastString.charAt(i)) {
+    i++;
+  }
+  const prefix = firstString.slice(0, i);
+  const prefixTokens = prefix.split(/[\s\\/]/);
+  const lastPrefixPart = prefixTokens[prefixTokens.length - 1];
+  return prefix.slice(0, prefix.length - lastPrefixPart.length);
+}
index 8c7e43cbc3278fce0d3052cad530b5e7e73f0926..4dd2393d3f1cefdc22df7716881a999b864ffdaa 100644 (file)
@@ -23,6 +23,7 @@ export interface IconProps extends React.AriaAttributes {
   className?: string;
   fill?: string;
   size?: number;
+  ariaLabel?: string;
 }
 
 interface Props extends React.AriaAttributes {
@@ -30,6 +31,7 @@ interface Props extends React.AriaAttributes {
   className?: string;
   size?: number;
   style?: React.CSSProperties;
+  ariaLabel?: string;
 
   // try to avoid using these:
   width?: number;
@@ -45,11 +47,13 @@ export default function Icon({
   height = size,
   width = size,
   viewBox = '0 0 16 16',
+  ariaLabel,
   ...iconProps
 }: Props) {
   return (
     <svg
       className={className}
+      aria-label={ariaLabel}
       height={height}
       style={{
         fillRule: 'evenodd',
index ed3df9a6404afe79624c5123cc611804fc80ae7f..092db41f94236e77b38c504a07765576a6f2cce3 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import { colors } from '../../app/theme';
+import { translate } from '../../helpers/l10n';
 import { Dict } from '../../types/types';
 import Icon, { IconProps } from './Icon';
 
@@ -53,12 +54,15 @@ export default function QualifierIcon(props: QualifierIconProps) {
 
   const qualifier = props.qualifier.toLowerCase();
   const FoundIcon = qualifierIcons[qualifier];
-  return FoundIcon ? <FoundIcon className={props.className} fill={props.fill} /> : null;
+  const ariaLabel = props.qualifier != null ? translate(`qualifier.${props.qualifier}`) : undefined;
+  return FoundIcon ? (
+    <FoundIcon className={props.className} fill={props.fill} ariaLabel={ariaLabel} />
+  ) : null;
 }
 
-function ApplicationIcon({ fill, ...iconProps }: IconProps) {
+function ApplicationIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M3.014 10.986a2 2 0 1 1-.001 4.001 2 2 0 0 1 .001-4.001zm9.984 0a2 2 0 1 1-.001 4.001 2 2 0 0 1 .001-4.001zm-5.004-.021c1.103 0 2 .896 2 2s-.897 2-2 2a2 2 0 0 1 0-4zm-4.98 1.021a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm9.984 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm-5.004-.021a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM2.984 6a2 2 0 1 1-.001 4.001A2 2 0 0 1 2.984 6zm9.984 0a2 2 0 1 1-.001 4.001A2 2 0 0 1 12.968 6zm-5.004-.021c1.103 0 2 .897 2 2a2 2 0 1 1-2-2zM2.984 7a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm9.984 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm-5.004-.021a1.001 1.001 0 0 1 0 2 1 1 0 0 1 0-2zM3 1.025a2 2 0 1 1-.001 4.001A2 2 0 0 1 3 1.025zm9.984 0a2 2 0 1 1-.001 4.001 2 2 0 0 1 .001-4.001zM7.98 1.004c1.103 0 2 .896 2 2s-.897 2-2 2a2 2 0 0 1 0-4zM3 2.025a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm9.984 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM7.98 2.004a1.001 1.001 0 0 1 0 2 1 1 0 0 1 0-2z"
         style={{ fill: fill || colors.blue }}
@@ -67,9 +71,9 @@ function ApplicationIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function DeveloperIcon({ fill, ...iconProps }: IconProps) {
+function DeveloperIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M7.974 8.02a3.5 3.5 0 0 1-2.482-1.017 3.428 3.428 0 0 1-1.028-2.455c0-.927.365-1.8 1.028-2.455a3.505 3.505 0 0 1 2.482-1.017 3.5 3.5 0 0 1 2.482 1.017 3.434 3.434 0 0 1 1.027 2.455c0 .928-.365 1.8-1.027 2.455A3.504 3.504 0 0 1 7.974 8.02zm0-5.778c-1.286 0-2.332 1.034-2.332 2.306s1.046 2.307 2.332 2.307c1.285 0 2.332-1.035 2.332-2.307S9.258 2.242 7.974 2.242zm3.534 6.418c.127.016.243.045.348.086.17.066.302.146.406.246.132.124.253.282.36.47.126.218.226.442.3.668.08.253.15.535.206.838.056.313.095.604.113.867.02.28.03.57.03.862 0 .532-.174.758-.306.882-.142.132-.397.31-.973.31H3.948c-.233 0-.437-.03-.606-.09-.14-.05-.26-.123-.366-.222-.13-.123-.306-.35-.306-.88 0-.294.01-.584.03-.863.018-.263.056-.554.112-.867a6.5 6.5 0 0 1 .207-.838c.073-.226.173-.45.298-.667.108-.19.23-.347.36-.47.106-.1.238-.18.407-.247.105-.04.22-.07.348-.086.202.13.432.277.683.435.342.217.756.4 1.265.564.523.166 1.06.25 1.59.25a5.25 5.25 0 0 0 1.592-.25c.51-.164.923-.348 1.266-.565.25-.158.48-.304.682-.435l-.002.002zm-.244-1.18c-.055 0-.184.066-.387.196-.202.13-.43.276-.685.437-.255.16-.586.307-.994.437-.408.13-.818.196-1.23.196-.41 0-.82-.065-1.228-.196a4.303 4.303 0 0 1-.993-.437c-.255-.16-.484-.306-.686-.437-.202-.13-.33-.196-.386-.196-.374 0-.716.06-1.026.183-.31.12-.572.283-.787.487a3.28 3.28 0 0 0-.57.737 4.662 4.662 0 0 0-.395.888c-.098.303-.18.633-.244.988a9.652 9.652 0 0 0-.128.992c-.02.306-.032.62-.032.942 0 .73.224 1.304.672 1.726.448.42 1.043.632 1.785.632h8.044c.743 0 1.34-.21 1.787-.633.447-.42.67-.996.67-1.725 0-.32-.01-.635-.03-.942a9.159 9.159 0 0 0-.374-1.98c-.098-.304-.23-.6-.395-.888a3.23 3.23 0 0 0-.57-.737 2.404 2.404 0 0 0-.788-.487 2.779 2.779 0 0 0-1.026-.183h-.004z"
         style={{ fill: fill || colors.blue }}
@@ -78,9 +82,9 @@ function DeveloperIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function DirectoryIcon({ fill, ...iconProps }: IconProps) {
+function DirectoryIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14 12.286V5.703a.673.673 0 0 0-.195-.5.644.644 0 0 0-.49-.203H6.704a.686.686 0 0 1-.5-.214.707.707 0 0 1-.203-.51v-.57c0-.2-.07-.363-.207-.502A.679.679 0 0 0 5.29 3H2.707a.672.672 0 0 0-.5.204.683.683 0 0 0-.206.5v8.582c0 .2.07.367.206.506.137.14.304.208.5.208h10.61a.66.66 0 0 0 .49-.208.685.685 0 0 0 .194-.506H14zm1-6.598v6.65c0 .458-.152.83-.475 1.16-.324.326-.7.502-1.15.502H2.647c-.452 0-.84-.175-1.162-.503a1.572 1.572 0 0 1-.486-1.158V3.654a1.6 1.6 0 0 1 .486-1.17A1.578 1.578 0 0 1 2.648 2h2.7c.45 0 .84.157 1.164.485.324.328.488.714.488 1.17V4h6.373c.452 0 .83.174 1.152.5.323.33.475.73.475 1.187v.001z"
         style={{ fill: fill || colors.orange }}
@@ -89,9 +93,9 @@ function DirectoryIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function FileIcon({ fill, ...iconProps }: IconProps) {
+function FileIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14 15H2V1l7.997.02c1 .034 1.759.758 2.428 1.42.667.663 1.561 1.605 1.574 2.555H14V15zM9 2H3v12h10V6H9V2zm3 10H4v-1h8v1zm0-2H4V9h8v1zm-1.988-5h3.008c-.012-.674-.714-1.443-1.204-1.937-.488-.495-1.039-1.058-1.816-1.055v2.96l.012.032z"
         style={{ fill: fill || colors.blue }}
@@ -100,9 +104,9 @@ function FileIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function LibraryIcon({ fill, ...iconProps }: IconProps) {
+function LibraryIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M1 13h4V3H1v10zm3-1H2v-2h2v2zM2 4h2v4H2V4zm4 9h4V3H6v10zm3-1H7v-2h2v2zM7 4h2v4H7V4zm4 9h4V3h-4v10zm3-1h-2v-2h2v2zm-2-8h2v4h-2V4z"
         style={{ fill: fill || colors.blue }}
@@ -111,9 +115,9 @@ function LibraryIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function PortfolioIcon({ fill, ...iconProps }: IconProps) {
+function PortfolioIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14.97 14.97H1.016V1.015H14.97V14.97zm-1-12.955H2.015V13.97H13.97V2.015zm-.973 10.982H9V9h3.997v3.997zM7 12.996H3.004V9H7v3.996zM11.997 10H10v1.997h1.997V10zM6 10H4.004v1.996H6V10zm1-3H3.006V3.006H7V7zm5.985 0H9V3.015h3.985V7zM6 4.006H4.006V6H6V4.006zm5.985.009H10V6h1.985V4.015z"
         style={{ fill: fill || colors.blue }}
@@ -122,9 +126,9 @@ function PortfolioIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function ProjectIcon({ fill, ...iconProps }: IconProps) {
+function ProjectIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14.985 13.988L1 14.005 1.02 5h13.966v8.988h-.001zM1.998 5.995l.006 7.02L14.022 13 14 6.004l-12.002-.01v.001zM3 4.5V4h9.996l.004.5h1l-.005-1.497-11.98.003L2 4.5h1zm1-2v-.504h8.002L12 2.5h1l-.004-1.495H3.003L3 2.5h1z"
         style={{ fill: fill || colors.blue }}
@@ -133,9 +137,9 @@ function ProjectIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function SubPortfolioIcon({ fill, ...iconProps }: IconProps) {
+function SubPortfolioIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14 7h2v9H7v-2H0V0h14v7zM8 8v7h7V8H8zm3 6H9v-2h2v2zm3 0h-2v-2h2v2zm-1-7V1H1v12h6V7h6zm-7 5H2V8h4v4zm5-1H9V9h2v2zm3 0h-2V9h2v2zM5 9H3v2h2V9zm1-3H2V2h4v4zm6 0H8V2h4v4zM5 3H3v2h2V3zm6 0H9v2h2V3z"
         style={{ fill: fill || colors.blue }}
@@ -144,9 +148,9 @@ function SubPortfolioIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function SubProjectIcon({ fill, ...iconProps }: IconProps) {
+function SubProjectIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M8 9V8h6v1h1v1h1v6H6v-6h1V9h1zm7 2H7v4h8v-4zm-1-7v3h-1V5H1v7h4v1H0V4h14zm-1-2v1.5h-1V3H2v.5H1V2h12zm-1-2v1.5h-1V1H3v.5H2V0h10z"
         style={{ fill: fill || colors.blue }}
@@ -155,9 +159,9 @@ function SubProjectIcon({ fill, ...iconProps }: IconProps) {
   );
 }
 
-function UnitTestIcon({ fill, ...iconProps }: IconProps) {
+function UnitTestIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
   return (
-    <Icon {...iconProps}>
+    <Icon {...iconProps} ariaLabel={ariaLabel}>
       <path
         d="M14 15H2V1l7.997.02c1.013-.03 1.57.893 2.239 1.555.667.663 1.75 1.47 1.763 2.42H14V15zM9 2H3v12h10V6H9V2zM7 8l-3 2.5L7 13V8zm1 5l3-2.5L8 8v5zm2.012-8h3.008c-.012-.674-.78-1.258-1.27-1.752-.488-.495-.973-1.243-1.75-1.24v2.96l.012.032z"
         style={{ fill: fill || colors.blue }}
index bf0355599b5580cce047c95404186150a42e2ef1..5754d4d99eb8d20895ebf42a4b8a9ebb266a18c7 100644 (file)
@@ -3220,6 +3220,7 @@ documentation.skip_to_nav=Skip to documentation navigation
 code.open_component_page=Open Component's Page
 code.search_placeholder=Search for files...
 code.search_placeholder.portfolio=Search for projects and sub-portfolios...
+code.parent_folder=Parent folder
 
 
 #------------------------------------------------------------------------------