From c98f196c3fdd40c21330ada0289188ca9eeee5a3 Mon Sep 17 00:00:00 2001
From: utkarsh_33 <60460-Utkarsh_33@users.noreply.drupalcode.org>
Date: Wed, 8 Jan 2025 22:22:19 +0000
Subject: [PATCH] Issue #3315859 by utkarsh_33, phenaproxima, narendrar,
 mtullo, rkoller, antonina pavlenko, chrisfromredfin, ressa: Communicate the
 status/state of a project in the button accesibly

---
 css/pb.css                                    |  20 +++++++---
 images/installed-check-icon.svg               |   1 +
 sveltejs/public/build/bundle.js               | Bin 289876 -> 293273 bytes
 sveltejs/public/build/bundle.js.map           | Bin 270424 -> 272193 bytes
 sveltejs/src/Project/ActionButton.svelte      |   3 +-
 sveltejs/src/Project/ProjectIcon.svelte       |  35 +++++++++++++-----
 .../src/Project/ProjectStatusIndicator.svelte |  14 +++----
 .../ProjectBrowserInstallerUiTest.php         |  19 +++++-----
 8 files changed, 60 insertions(+), 32 deletions(-)
 create mode 100644 images/installed-check-icon.svg

diff --git a/css/pb.css b/css/pb.css
index aa5073fa6..84c5e86b0 100644
--- a/css/pb.css
+++ b/css/pb.css
@@ -420,10 +420,6 @@
   line-height: 0.75;
 }
 
-.pb-actions__icon {
-  color: #228572;
-}
-
 /* <Project/Categories> */
 .pb-project-categories__list {
   display: inline-block;
@@ -716,8 +712,12 @@
 
 /* <Project/ProjectStatusIndicator> */
 .project_status-indicator {
-  text-decoration: none;
-  font-weight: bold;
+  display: flex;
+  align-items: center;
+  padding: 0.25rem 0.5rem;
+  cursor: not-allowed;
+  border: none;
+  background: none;
 }
 
 /* <Search/FilterApplied> */
@@ -1038,3 +1038,11 @@
     border: 1px solid;
   }
 }
+
+.project_status-indicator__label {
+  margin-bottom: 5px;
+  margin-left: 5px;
+  color: var(--color-gray-800);
+  font-size: 16px;
+  font-weight: 700;
+}
diff --git a/images/installed-check-icon.svg b/images/installed-check-icon.svg
new file mode 100644
index 000000000..acc80a1af
--- /dev/null
+++ b/images/installed-check-icon.svg
@@ -0,0 +1 @@
+<svg fill="#55565B" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6.464 13.676c-.194.194-.513.194-.707 0l-4.96-4.955c-.194-.193-.194-.513 0-.707l1.405-1.407c.194-.195.512-.195.707 0l2.849 2.848c.194.193.513.193.707 0l6.629-6.626c.195-.194.514-.194.707 0l1.404 1.404c.193.194.193.513 0 .707z"/></svg>
diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js
index 2598ec17d590139dfd37105838e127d5c8677a42..31ddef62d28f2f60a6c4acb391bb1f0e8701fef6 100644
GIT binary patch
delta 1534
zcmZ8hdrZ}37~c1O=KzQML6DmVeq8bZ0iMG(K<3$^VUb+Cv5^P|#9#sv9503UB(&v3
zf6gaTjGD7@I53hgQ75e}#0y}tWwQkTXi=LA!wZ>W-vKq(_t(4cy?pQUywCRzUDG#D
z=sT7+vsEZIQV6m(P(Wu>b3B(9A>Ayp^VZ>oT50E<<$hRONbb0k$#rPE&RsfdKL65>
zmEzF~9>S6kRLHBavQRqz%HtlLJ!j6iU~UI_VKARNt6Q7(Sg@NMJKKBnn7Xui=DKrE
z-^q;qeO_YNjagFm_c>s0Hwzc9XR?eKwZ7L&d}?DmbqLO64)|e!D{vj;W~8OD7;L`E
z9bvi3ebIV=C!k;(bHT_}*<|Y<OhJ!3e*@=!<a6Qa$1N!QNm@#>W}%RAwZyC#KTO`Z
zxr6zzL<F`_zQ}%`?TSO#7Um^JPqB>_mLjb8*(_oyFJNELBi|d8EQp>-78!;d9n<6Q
zKx)CCfuvXakGUdeJM%`HmYR@hpinGwq-^z<D?aEmP#!7_q#z)O8r8vbzBoTYI#gSj
z4xT@AC+rKN0Gys64<Bo7g*9eXMV+;zvTRjov=VBoT~~&X8!R1T7G}U6gPenhLe7YF
zqIs|clOtNQnG^JW8h^)VrLM+WZNg{4<bxf-G<({oMzscN?GCe$j&Kc3ZuBa~DrHQu
zmFv`=>pBq|PQMZA$0$>+>2<*#8}qV5OvdfA6d+>9$;#D{dqJZ8F6DE0E@9EgouqJ#
zw39Z-oN9_x>cc8lE4FuQ%9Ql<bj4UvU1zgaN)Sf%=7nGs-<Naz^%QeOdj@&n)($xp
zducBFLe@B$appS;R!_GW#MyD$!qnqO^tf%LS-7&3dppF~+YiOCk!HKtDr}Wyk{Wwv
z{R5hh{f8(QW5*;3)eYPMb$4mbvtFIa-=0}Col~^lTDh)F36bqZJWXEYWp~?Yexc}%
z2NW?4Q9XVr4Mj#N<j42qv^QU*G=m*s)I6osw(%`f;s2o`uZjF9{FRxHkzoHAd(m1#
zCAhqg3Q&AmDsPKXPJQ@mN`|SGv{+ljFJQo3W5J={xgMrAsmgYhik^=KD<dgZd^1U3
z@VIDaMIN2*Ik~Ay{nIO5jn!73`U3AUrR8O%Yp~!Uy@dWoDU-d~<c3WT$q&Jg$m72n
zBKHvuGF*$2s25IAI?msg|I67(%a~Qj%`nA~aFn9Ohmn-caI%dex?ZNHP?jL}Eh2-K
z#fkRCv_Ydan^NQB@s1!xWaU$;6D6BOV-a;Yu|zeuH%gS0(^nd17Oqt^p=C*3b@Z6C
zcrmq})-aYZ9dckJSu~o&)TES|I+L3y(w-8!otA63*<=#$HBcef#HS`C%|!gOi_DxQ
ziI|V*ZH_0k<S$O_rPUgibg+*6MDzhF;4DtszTya|izK_tKB1j>vRE<_P{lpa5~a~W
zxzDr**Kw2cb7n)awTY)9dM$T>C6N2f?3!G6?uI4hn%%hL%g<wQIr$@Zy%gQ|6&e$A
zjO2-ei&8Je{@fjxhIj@B@+nH(IZUrPBC%hxSbUW7oZvkq$<E2;CZRh)c?Ntu{8VH@
zitJmC?ooPEZ0{#ki?A_@5c7v<g$pi?Ju592j~h0Cdt%cZX?5(9yH{e+W1F67x<;tm
e2_H;K*^G`-nj@4c`C68&)7UUYqG5^;RQ(GNU>yqp

delta 1091
zcmXw2Z%ou>9N+VOes|n?9C#=HEbh2N2j~#*f880x4G3)rxR`BD<Ss=-<tV%rWnqvT
zOr?NpeMUk$+)7&m?Xri230p4|)+AdmY=LGkELQB?+<ZaVdVUn|zW@JyKHtGdrXTN^
zF4qkX%$PA2q_vosCq4Rp=B9YxpqmfOXpwh~ZHzAsZP%<QHGEyXdSo_>y@KFjZf9QH
z`i}g#JWdMEALb5x@jw=+I>l4t(-RX0^hTK(``Y=Mm7?glKuPhj8@m{CDw!5NK1yEk
z#<ORxlw(5&GmCphR_bSA*srloVn-z_--4GN+$_#T*-1S@k7e5YRhEp*1)hfbe6EL0
zwS^XVE?T}Mj~MM>R)ze9xZgzUaKDSCGY@Lps6?G|y{LYV^|+AON@nr<Rn}gridu2-
zF-xHp5qWaQSoKIMuFvoc(Xhzs2-9tHf~>#TZ5xnHCRo<<WF%!%9|B%(L`^44Lra3^
zBauy~QDh|>hAq^OYAbcaewP);`aV{e6TkdKnfN4!@|9z+h9kKC6wAfC16*}YJ<Zcs
zWuuK;tFk@#zg@g-qf|wsCOSp`MhbH52vQxod~y{D-V(!5fQrOy8C5aNmr{W!si3}G
z<`#2JWYI9ESUO5?C`k@~!0p~-!|knP5k0TbHXZr=I7CXG_~T7FrDL90?TyXid>36(
zm_xkSO^-CpD@uCl30GVJryIduO2fhx+8|!<CnsZWH38+ra?;VLqQi8z^v`wCOJaA7
zCb`xV@cCC8dO@4jvZ1rITf*cQ-<+d*u5|@GzSR)_1#)rO@#v@2#3gvLbC&q?5*<_|
z-&T|(qqK+1qe&%G$U(j)8TR2<bW&xp7T@aSUH2U4Ia%tpilU9}(X!S^v@sI3729%u
z9g5{5=puVccz?9HHFB(>p?QC6MA{Z8!w_z34sSWseBeN11kdGAg+wkLp^LNzp-#RH
z@1Ky!-?~fbDkt=~w=AD#I7nuEAK@wZdmlx_3*%IggrPDj6k>{Y>5x$&v2fg^P7B6`
z40U`?&i}}0d_X5SPES!GiWP1XcNQt8f$pY^k0ofYfjui8E|Xo4GmBIu>J@HD!jS|O
zV8p-+Fj>!a_#vMvF(%~KC6;CF8a;bPv=q>B4&wxwW0Ox((CVdR@s6ARVPb6&?PPfP
z8~I|jAEm)zq*R>AlDMf1he_d*KLPrT<046qby=<$+RLqC=kxRd!&_=sB}m2Hvj#3^
I4a!{0zk*41kpKVy

diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map
index fc9f20a4d9dab79018cb86d872583c7930061a13..358482615ee65b140b0aa3278c019d4546fef7ec 100644
GIT binary patch
delta 1071
zcmah`OKcNY6xEySj2#1w)6|eODj6q|&F2M2CLupU`^KIcm!xqTL=ad_C&>_-8pn7h
zr8KgH)I}<#w4flaC@sWmq$;4$QKZn8U8_on4dJsWgaxb$R6+t8FZ#wNR(8zd>D_bB
zJ?EY`cYb$Wzu}sl7p9X?H+>Tv)APcmucyGA0%DuTmSy6?pB6}sTT(L#wQ(e&t3yd`
zq$^f$WX3g|TOa}Zu2@RqAO5pIc2`*Uoqj#7rV22Aw?MqU!NH_Dq$OkZyC!r)HOBS5
z+LuPQMBC{f8aIrw6fQj?TTRysQe~r(lt^)IlACo%6GaC}N~=b^L-Lhcjl*$m_;cL$
zifk;8tne}WlGNhH|A@<7B5w0-rbUq~#S@dn-7wLRI3yXz)0))P)wN2}mn}rF5}>DA
zaAS0ttOZ`;r_03a;OP=aMq?>FeUf<b=Vj7_4^EN>41Y_i@U1)v^Uc+yQS|Z`-k_9o
zqC-pST2cRX;uVMHHw$Bbp47P{X+`w4!m}r&#yotD^uLSO7Kt5iz9Ue);1&4ID^!}d
zZj&Zaie|<RYr{rlcq~=O%>Q5MX=3$~-|YUI%vDdG%M<flUbw4cZZ}By$5B{Ew&P41
z4mmnIM}sUpDx;JaJm_i!TX`>wZV%BO)`z+GU<;nw1R}>ij&dl{i%pB*#tI5+aQ3-i
z6D^b_(7y=li|9-lye$S4_ECEXb5Di3Rc`z6#5A~Ya2{&J9m*=#51`UEz=qn9`~|gm
zDFbadTLxv~@lfzcXuml)3;zPn{08M5VV|^zEToO_-J3(KJ1*mcJ>Wij1zPQ2DAwM+
zn0_j3cJLhCyzIf;J@A;PuD}K&9{X_hKqJ0C2bHul!a`|<DvU9zT1S|%2<>BR4;^4^
zpxEf;jVMhZIS&Cy1rOk94^&~r2-wZ^9CU!chbgTJ?PsjNFu+BFQA>%@h$Y9>6{^dO
z>9W;o!TVRij~BlIr=6GaAPr+*2&fp~lz@zvA40voSq`?!tuihxLG>>>IGv==T$6>p
zPSS3EHVI#?J?n#7M}#G1zf2YU{!gAWd<O2>;8-w&J96Mb=Z~-nqg!AdPMwEe&9)z)
VX1y2*w(to&cpd7@$0i(&{|{1~UN`^%

delta 569
zcmYj~L1>b37{+<O_fu<z8l#$nrqkGAxoIw|v1R=ICTh!>FbYDNPGvLfoB4gKt=eks
z(7`n<8Sin>spybF^k*=`TI<&65_BsHI*)=5gIJ$P(0h0e@9*LFyxFpRu3M^WLglOQ
ztnv}fmAcTjSVCSx5Z6fm9!!+4!6Evv4qih-wrl4neGS4#2Hi>_B{yFFM%+@=lsc`9
zW@ps7gxrLFKDG&&cK-Vdg!Q3D-JZ^;6y<cnQ5f$@rPbV&tc;E2(o=H6VNVrvsh-JP
zCX<!5f?Ax;CLI1Q=P{4R)7Ryk@p_t4e)$KyY@^;dSoo(Jw2E}+E$pY|buCQq;EaAS
zcXc8mlVJl!>DvRaQD{j+4>ur0-J5V2+_YMQF4}bo&ilSy;J#hql>);QSJ4GNnpg1x
z)mPE9XJBALVxj&3jTl8M)vefKiLvwJt|*JgToPl<6{Q6mw$e^F9?;OpISoamCK@IP
zVh35AXx4dyEFAjJzbO2ZmNRG-FS2n)@Doo_^d6cixg!{L#{*I%LY=p<osuWez?T*f
zT0r8<B^+%5H~+qZH}@H1EOSNDM%lF>dG?A&soJ5<Mc7$6$mbv99}#DzB9)(GCsm)~
zFaGEWS`Rh`42Ibu>0}@;(bf%Ip=}h6bk~MXe(5^)qCUd<1ARlpwuM%noI{2b4x&{T
Ok>Z6QfAJd2lYaqPzq-Hx

diff --git a/sveltejs/src/Project/ActionButton.svelte b/sveltejs/src/Project/ActionButton.svelte
index 3e97859e5..45b759356 100644
--- a/sveltejs/src/Project/ActionButton.svelte
+++ b/sveltejs/src/Project/ActionButton.svelte
@@ -10,6 +10,7 @@
     updated,
     activeTab,
   } from '../stores';
+  import ProjectIcon from './ProjectIcon.svelte';
   import LoadingEllipsis from './LoadingEllipsis.svelte';
   import { processQueue } from '../QueueProcessor';
 
@@ -62,7 +63,7 @@
     <ProjectStatusIndicator {project} statusText={Drupal.t('Not compatible')} />
   {:else if project.status === 'active'}
     <ProjectStatusIndicator {project} statusText={Drupal.t('Installed')}>
-      <span class="pb-actions__icon" aria-hidden="true">&#10003&#x20</span>
+      <ProjectIcon type="installed" />
     </ProjectStatusIndicator>
   {:else}
     <span>
diff --git a/sveltejs/src/Project/ProjectIcon.svelte b/sveltejs/src/Project/ProjectIcon.svelte
index 8628d1ce4..3af92e566 100644
--- a/sveltejs/src/Project/ProjectIcon.svelte
+++ b/sveltejs/src/Project/ProjectIcon.svelte
@@ -30,15 +30,32 @@
       alt: Drupal.t('Well maintained'),
       title: Drupal.t('This module is actively maintained by maintainers.'),
     },
+    installed: {
+      path: 'installed-check-icon',
+      alt: Drupal.t('Installed'),
+      title: Drupal.t('This module is installed.'),
+    },
   };
 </script>
 
-<button class="pb-project__status-icon-btn" title={typeToImg[type].title}>
-  <img
-    src="{FULL_MODULE_PATH}/images/{typeToImg[type].path}{DARK_COLOR_SCHEME
-      ? '--dark-color-scheme'
-      : ''}.svg"
-    class={`pb-icon pb-icon--${variant} pb-icon--${type} ${classes}`}
-    alt={typeToImg[type].alt}
-  />
-</button>
+{#if type === 'installed'}
+  <span class="pb-project__status-icon-span" title={typeToImg[type].title}>
+    <img
+      src="{FULL_MODULE_PATH}/images/{typeToImg[type].path}{DARK_COLOR_SCHEME
+        ? '--dark-color-scheme'
+        : ''}.svg"
+      class={`pb-icon pb-icon--${variant} pb-icon--${type} ${classes}`}
+      alt
+    />
+  </span>
+{:else}
+  <button class="pb-project__status-icon-btn" title={typeToImg[type].title}>
+    <img
+      src="{FULL_MODULE_PATH}/images/{typeToImg[type].path}{DARK_COLOR_SCHEME
+        ? '--dark-color-scheme'
+        : ''}.svg"
+      class={`pb-icon pb-icon--${variant} pb-icon--${type} ${classes}`}
+      alt={typeToImg[type].alt}
+    />
+  </button>
+{/if}
diff --git a/sveltejs/src/Project/ProjectStatusIndicator.svelte b/sveltejs/src/Project/ProjectStatusIndicator.svelte
index 08c02b514..2e812ce9e 100644
--- a/sveltejs/src/Project/ProjectStatusIndicator.svelte
+++ b/sveltejs/src/Project/ProjectStatusIndicator.svelte
@@ -5,12 +5,12 @@
   const { Drupal } = window;
 </script>
 
-<span class="project_status-indicator">
+<button class="project_status-indicator" aria-disabled="true">
   <slot />
-  <span class="visually-hidden"
-    >{Drupal.t('@module is', {
+  <span class="visually-hidden">
+    {Drupal.t('@module is', {
       '@module': `${project.title}`,
-    })}</span
-  >
-  {statusText}
-</span>
+    })}
+  </span>
+  <span class="project_status-indicator__label">{statusText}</span>
+</button>
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
index 16225bf5f..1f7582c06 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
@@ -82,8 +82,8 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $this->assertSame('Install Cream cheese on a bagel', $download_button->getText());
     $download_button->click();
     $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000);
-    $this->assertTrue($assert_session->waitForText('✓ Cream cheese on a bagel is Installed'));
-    $this->assertSame('✓ Cream cheese on a bagel is Installed', $installed_action->getText());
+    $this->assertTrue($assert_session->waitForText('Cream cheese on a bagel is Installed'));
+    $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText());
 
     // The activator in project_browser_test should have logged a message.
     // @see \Drupal\project_browser_test\TestActivator
@@ -236,8 +236,9 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.project__action_button");
     $cream_cheese_button->click();
     $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000);
-    $assert_session->waitForText('✓ Cream cheese on a bagel is Installed');
-    $this->assertSame('✓ Cream cheese on a bagel is Installed', $installed_action->getText());
+    $assert_session->waitForText('Cream cheese on a bagel is Installed');
+    $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText());
+
   }
 
   /**
@@ -273,8 +274,8 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.project__action_button");
     $cream_cheese_button->click();
     $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000);
-    $assert_session->waitForText('✓ Cream cheese on a bagel is Installed');
-    $this->assertSame('✓ Cream cheese on a bagel is Installed', $installed_action->getText());
+    $assert_session->waitForText('Cream cheese on a bagel is Installed');
+    $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText());
   }
 
   /**
@@ -318,7 +319,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000);
     $this->assertNotEmpty($installed_action);
     $installed_action = $installed_action->waitFor(30, function ($button) {
-      return $button->getText() === '✓ Cream cheese on a bagel is Installed';
+      return $button->getText() === 'Cream cheese on a bagel is Installed';
     });
     $this->assertTrue($installed_action);
   }
@@ -392,13 +393,13 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
 
     $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000);
     $installed_action = $installed_action->waitFor(30, function ($button) {
-      return $button->getText() === '✓ Cream cheese on a bagel is Installed';
+      return $button->getText() === 'Cream cheese on a bagel is Installed';
     });
     $this->assertTrue($installed_action);
 
     $installed_action = $assert_session->waitForElementVisible('css', "$kangaroo_module_selector .project_status-indicator", 30000);
     $installed_action = $installed_action->waitFor(30, function ($button) {
-      return $button->getText() === '✓ Kangaroo is Installed';
+      return $button->getText() === 'Kangaroo is Installed';
     });
     $this->assertTrue($installed_action);
 
-- 
GitLab