From 6b45a2e5d041ae4a62634a7b9110ba1e15a9479f Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Mon, 25 May 2026 22:05:00 -0600 Subject: [PATCH] Issue #59 fixes --- app/(marketing)/blog/[slug]/page.tsx | 2 +- app/components/controls/Chip/Chip.view.tsx | 1 + app/components/navigation/Footer.tsx | 37 +++++++++++++----- .../MenuItem/MenuItem.container.tsx | 8 ++-- .../sections/LogoWall/LogoWall.container.tsx | 6 --- .../sections/LogoWall/LogoWall.view.tsx | 8 +--- content/blog/_template.md | 2 +- ...ing-burnout-sustainability-in-the-ruins.md | 2 +- ...gital-mediation-and-the-death-of-nuance.md | 2 +- .../blog/how-chaos-concentrates-control.md | 2 +- ...ntegrating-new-members-without-dilution.md | 2 +- ...ge-management-and-institutional-amnesia.md | 2 +- .../making-decisions-without-hierarchy.md | 2 +- .../blog/operational-security-mutual-aid.md | 2 +- content/blog/resolving-active-conflicts.md | 2 +- lib/assetUtils.ts | 3 +- messages/en/common.json | 3 +- messages/en/components/footer.json | 17 +++++--- .../en/create/community/communitySave.json | 4 +- .../create/community/communityStructure.json | 2 +- messages/en/pages/useCases.json | 2 +- public/apple-touch-icon.png | Bin 2178 -> 1807 bytes public/assets/logos/gitea.svg | 13 ++++++ public/assets/logos/mastodon.svg | 6 +++ public/favicon-16x16.png | Bin 211 -> 239 bytes public/favicon-32x32.png | Bin 339 -> 375 bytes public/favicon.ico | Bin 5430 -> 5430 bytes scripts/generate-favicons.mjs | 34 +++++----------- stories/cards/Icon.stories.js | 4 +- .../navigation/Footer.responsive.stories.js | 22 ++++++++--- stories/sections/Groups.stories.js | 2 +- stories/sections/LogoWall.stories.js | 10 ++--- tests/components/Footer.test.tsx | 7 +++- tests/components/Icon.test.tsx | 6 +-- tests/components/MultiSelect.test.tsx | 24 ++++++++++++ tests/e2e/critical-journeys.spec.ts | 4 +- tests/pages/learn.test.tsx | 4 +- tests/pages/user-journey.test.jsx | 16 +++++--- tests/unit/LogoWall.test.jsx | 28 ++++--------- 39 files changed, 167 insertions(+), 124 deletions(-) create mode 100644 public/assets/logos/gitea.svg create mode 100644 public/assets/logos/mastodon.svg diff --git a/app/(marketing)/blog/[slug]/page.tsx b/app/(marketing)/blog/[slug]/page.tsx index 00584fb..1ce8706 100644 --- a/app/(marketing)/blog/[slug]/page.tsx +++ b/app/(marketing)/blog/[slug]/page.tsx @@ -126,7 +126,7 @@ export default async function BlogPostPage({ params }: PageProps) { headline: post.frontmatter.title, description: post.frontmatter.description, author: { - "@type": "Person", + "@type": "Organization", name: post.frontmatter.author, }, publisher: { diff --git a/app/components/controls/Chip/Chip.view.tsx b/app/components/controls/Chip/Chip.view.tsx index 7620104..d41a91e 100644 --- a/app/components/controls/Chip/Chip.view.tsx +++ b/app/components/controls/Chip/Chip.view.tsx @@ -155,6 +155,7 @@ function ChipView({ tabIndex={0} onClick={handleClick} onKeyDown={(e) => { + if (e.target instanceof HTMLInputElement) return; if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handleClick(e as unknown as React.MouseEvent); diff --git a/app/components/navigation/Footer.tsx b/app/components/navigation/Footer.tsx index 02a7529..a897281 100644 --- a/app/components/navigation/Footer.tsx +++ b/app/components/navigation/Footer.tsx @@ -25,7 +25,7 @@ const Footer = memo(() => { /** Figma 18411:62925 (1024+): org name is one line, `w-full whitespace-nowrap`. */ const orgNameClass = `${bodyTextClass} lg:whitespace-nowrap`; - const primaryLinkClass = `inline-flex w-fit max-w-full self-start text-[var(--color-content-default-primary)] font-inter text-base font-medium leading-5 tracking-[0%] ${linkFocusClass} p-2 -m-2 cursor-pointer lg:text-2xl lg:font-normal lg:leading-7`; + const primaryLinkClass = `inline-flex w-fit max-w-full shrink-0 whitespace-nowrap self-start md:self-end text-[var(--color-content-default-primary)] font-inter text-base font-medium leading-5 tracking-[0%] ${linkFocusClass} p-2 -m-2 cursor-pointer lg:text-2xl lg:font-normal lg:leading-7`; /** Figma 18411:62944: 40px gaps, w-[396px] link block; `p-2` on links overruns 396px—tighten x at `md+` row. */ const legalLinkClass = `inline-flex w-fit max-w-full self-start text-[var(--color-content-default-secondary)] font-inter text-sm font-normal leading-5 tracking-[0%] ${linkFocusClass} p-2 -m-2 cursor-pointer underline decoration-solid [text-decoration-skip-ink:none] md:self-auto md:px-0 md:py-1 md:mx-0 md:text-xs md:leading-4 md:whitespace-nowrap md:no-underline md:text-[var(--color-content-default-primary)] lg:text-sm lg:leading-5 lg:text-[var(--color-content-default-primary)]`; @@ -37,7 +37,11 @@ const Footer = memo(() => { name: t("organization.name"), email: t("organization.email"), url: t("organization.url"), - sameAs: [t("social.bluesky.url"), t("social.gitlab.url")], + sameAs: [ + t("social.bluesky.url"), + t("social.gitea.url"), + t("social.mastodon.url"), + ], }; return ( @@ -104,27 +108,42 @@ const Footer = memo(() => { {/* eslint-disable-next-line @next/next/no-img-element -- social logo */} Bluesky -
{t("social.bluesky.handle")}
+
{t("social.bluesky.label")}
{/* eslint-disable-next-line @next/next/no-img-element -- social icon */} GitLab -
{t("social.gitlab.handle")}
+
{t("social.gitea.label")}
+
+ + {/* eslint-disable-next-line @next/next/no-img-element -- social icon */} + +
{t("social.mastodon.label")}
diff --git a/app/components/navigation/MenuItem/MenuItem.container.tsx b/app/components/navigation/MenuItem/MenuItem.container.tsx index 0101668..b340ee7 100644 --- a/app/components/navigation/MenuItem/MenuItem.container.tsx +++ b/app/components/navigation/MenuItem/MenuItem.container.tsx @@ -70,15 +70,15 @@ const MenuItemContainer = memo( "border border-[var(--color-border-default-brand-primary,#fdfaa8)] text-[var(--color-content-default-brand-primary,#fefcc9)] bg-transparent hover:bg-[var(--color-gray-800)]", }; - // State styles for Inverse mode (black text on yellow background) + // State styles for Inverse mode (black text on yellow HeaderTab / inverse surfaces) const inverseModeStyles: Record<"default" | "hover" | "selected", string> = { default: - "bg-transparent text-[var(--color-content-inverse-primary,black)] hover:bg-[var(--color-surface-brand-accent,#4d4a00)] hover:text-[var(--color-content-inverse-primary,black)]", + "bg-transparent text-[var(--color-content-inverse-primary,black)] hover:bg-[var(--color-surface-inverse-brand-secondary)] hover:text-[var(--color-content-inverse-primary,black)]", hover: - "bg-[var(--color-surface-brand-accent,#4d4a00)] text-[var(--color-content-inverse-primary,black)]", + "bg-[var(--color-surface-inverse-brand-secondary)] text-[var(--color-content-inverse-primary,black)]", selected: - "border border-[var(--color-border-default-primary,#141414)] text-[var(--color-content-inverse-primary,black)] bg-transparent hover:bg-[var(--color-surface-brand-accent,#4d4a00)]", + "border border-[var(--color-border-default-primary,#141414)] text-[var(--color-content-inverse-primary,black)] bg-transparent hover:bg-[var(--color-surface-inverse-brand-secondary)]", }; // Get state styles based on mode diff --git a/app/components/sections/LogoWall/LogoWall.container.tsx b/app/components/sections/LogoWall/LogoWall.container.tsx index 29e4168..4e9c079 100644 --- a/app/components/sections/LogoWall/LogoWall.container.tsx +++ b/app/components/sections/LogoWall/LogoWall.container.tsx @@ -20,37 +20,31 @@ const LogoWallContainer = memo(({ logos, className = "" }) => { src: getAssetPath(partnerLogoPath("food-not-bombs")), alt: t("partners.foodNotBombs"), size: "h-11 lg:h-14 xl:h-[70px]", - order: "order-1 sm:order-4", }, { src: getAssetPath(partnerLogoPath("start-coop")), alt: t("partners.startCoop"), size: "h-[42px] lg:h-[53px] xl:h-[66px]", - order: "order-2 sm:order-2", }, { src: getAssetPath(partnerLogoPath("metagov")), alt: t("partners.metagov"), size: "h-6 lg:h-8 xl:h-[41px]", - order: "order-3 sm:order-1", }, { src: getAssetPath(partnerLogoPath("open-civics")), alt: t("partners.openCivics"), size: "h-8 lg:h-10 xl:h-[50px]", - order: "order-4 sm:order-5 md:order-6", }, { src: getAssetPath(partnerLogoPath("mutual-aid-co")), alt: t("partners.mutualAidCo"), size: "h-11 lg:h-14 xl:h-[70px]", - order: "order-5 sm:order-6 md:order-5", }, { src: getAssetPath(partnerLogoPath("cu-boulder")), alt: t("partners.cuBoulder"), size: "h-10 lg:h-12 xl:h-[60px]", - order: "order-6 sm:order-3", }, ], [t], diff --git a/app/components/sections/LogoWall/LogoWall.view.tsx b/app/components/sections/LogoWall/LogoWall.view.tsx index 2384436..5da57d7 100644 --- a/app/components/sections/LogoWall/LogoWall.view.tsx +++ b/app/components/sections/LogoWall/LogoWall.view.tsx @@ -14,18 +14,12 @@ function LogoWallView({ className={`p-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-024)] md:py-[var(--spacing-scale-032)] lg:px-[var(--spacing-scale-064)] lg:py-[var(--spacing-scale-048)] xl:px-[160px] xl:py-[var(--spacing-scale-064)] ${className}`} >
- {/* Label */} -

- Trusted by leading cooperators -

- - {/* Logo Grid Container */}
-
+
{displayLogos.map((logo, index) => (
@O(Rcu^#Fd5pW!mX<#@RpK_r34?em?Jg-pqIA zUH13$GBPkT0D(Y8KHm61z;|hFHyAii$-u_}r=RQ{o(cl(IiNKiP;LDo5C}ZzgZCh3 z)W71MYM~M=yk@?`eHjJG4^yJ{e@5_->EXKeOZ)_aPJh9awFK9-PZ-ExI2P{f+V`!E z`}zk(%WnHPI?LgvL&KFDU!O6}#C*8l?n(0>++=RbpUl_m23hU&uW%>0{cqwNcL5Uq z1&hBEjCy_Tn)CZDFWqBcelitxJ(zuIpEZS+98ZSem%p$d=#8zNGx>>LN;A@}`)iR;klH>)-A8;yFc?(WNUB<>Q2mH- za*T)H$Bc>$-6#sSa9Hon5P1_gtlZcwPT(G$0SdhlJK!hB-t6M8Z8 zWZ0IWGCJjIgdW*U_^LqxnKQ7Fg-DETWN>XHYqliU2AQB+9HludYw0ALw<;0`y8R5< z^d2`SxkeR2UfbuUP$Kn(ebu!@r04+o&aOtx$m@{vHW87_Wy+`euSC^;4ycjTVIFd; z1(Ke+As!&+Vf`~FQN=mLN-KVH&&Bf(=iOUKp`?d(%kBb<1M_49B-eI3U0%|k3=Um| zRCDf?lju&rT##4JpbP0$s#O&xRuXAl&~njW9z7zpyA)zeYlZ+nz#Ym%Az95Fle7>0@7zpY4G0_i-ju1SqYrQUvVy2c`#M^+Nqq##F*Oy3Wiuam;dg^`xQKQOcJio<%llts?v{lVA~ zm>1OHBdl5M-}~bdnX$Uqf9mlwzM`|;(`A4{y+OUtv`H&>E=sy$Go*^*Zv$(u#_b8rF|0R3Z#lm}%goFTD=sfDQ zLo0}{A*S4C9_IC|PB+zB$pgb$7bru?tzt%I1us>X(7w1fwTBOXy_4{s^Y&cI^Yx+A z^G0FnSq#Gb@rRfImFa4o`A2R}R;er? z;=FhidiUP5QMH@6x;O27W#FN`v_<8#?6ko0X!lHJ_oI(9 z|G7D}W51CxzGZo2gkDynrmZ~woP8jkNta^W8z6Vlrzw$>D;^bkXI4mds>~%#tz-Z5 zbv!(&#@!4n359-%hv#y4G6V7gjBk?`T&It&ziW3VEc^jG%t(>=S53h_xzloX;ueR<;=TgEiFZBF-I#VePPDFpPey33m-Cb|ei)q0i z=KjV-bwJ0SN3G?>QK*+sF*lE+6VbWR6pDTY44mc}Yui*LRx8_jhLr{mw>=*1#CoHusLZ*;|$7)Ct6 zb5Ts!uo=^exgn9f*D8CoU?0jxQSbaIrw!IUovrxv1k|fQrE;d5PgC*aud`X#I-8UGcwGA_|PS z(=~0M|9`N*>zNugE>_@yvbZ(+EP^gNsBHx<#Ug+2prhR$ZdSHQ$NrnH1FRF!p~v^% X-10X1iuPMct@iFi@WZ#`qVoR-3c_DI literal 2178 zcmcgu`#;m|8=vpW$YJ$-#nYTtiN{wCdF0TTEvHIKNeELYW-CHX$yxEFq*grA+T`?2 zIop=Q9P)~UnR$f?-^grX&W9GZ@4e^wAD$oX&-K3U>$=|8`?_DR&;7dpa6jduvIDUL z0)eO;KjwHA*jm|AkOyD89(i+M6eEuLL_r`>p=`;~W6u&G5Cx0lj@WZCFGQn+^z)u- z?Tbb3ct@f8sbI(BPI_VPN57?eApgxjkbHm6N#V=|PKUy)?sTE6+9+Lfn?JJt`P74( zmkxG*dR48YfOS_p<8$u{V?tR$e~;zz%dJ_~nzU_M{MKhf@GW;4*WTT&h^n+kWx+8+ zNl?T#wWK}{vU1@c_d})oMo0a}?3pfGDZji4gyBtA+Q04ykgk!NE{E~Le=vByln9bwxa3iC!S9CLAB*78*~%n5}F&$&Wn=p#%R>I(fbDe$oq zT3$d572op=cVP0kEtX>5dBu8BwmhS=_uYRuuR%=TUJ!;UA>uFQZ8o;0Tr+>Ce^pi* zqJyX{ElRq&R0CJMFe!%UatjxsDNRXDrAxXunPK!GMx zE4BbvQ@;uHU59H|fwWc0*gN0=VjS1HtZH^q#5<|vub|Xz9)_8)S zYdMQka@u~(!0ov(sQtkn_N6qpg{CHt&0v%ux4G!ho-*Ek!<#BY!#A=AH>WVnfQMF!nl@Iu(oum3-()6Q9$f!Jk@MG z>rAg*P`jc;WR()h!C4Z;5GQ7Iln|yJjp?{TwG>%P7dM!bf3p0BmgFvUmOc|PR|b90 z-y)H`Izj@>!ZWq1a+G}0m@4Bqc5=UOp0sMWD?gES5@g>eepi{HBI1=)Co-P& z=!_Fm)3}yk=RQ#~c{fv=b3lVaAwB^*@i8@Q3>TxHH#IvXgxaQN8BOgjm+JsrNSO#!=Z9MwnQX47!lVHU< z>su0Ae8wx&hdQXVFL5kN@y{o6e$yd|I9pQ8a$e`n$9?`Q+ zCGyopm9IJwEJ_m5kR@z&z8Zqed!M00tOYrSJ;FX`JlGg<9iX}X1y1au!3J&jb?gWx zqZH)o#rO6e_D1mjx}&Nnb#$}3jD1+a02}l7CB>D2yAxOe7|XIQ#fjlOku13jeZzZIS8xPNrYugy{`#*)JVYx+aP+a`L(}i@x2=p zC;!LBWba>0eiDv?`ee2#os0)!Y<8Ghi)9JF+G18@{=eW)qcviKBDQC(!sc4ZQ%6Hq zdI}$}ripgz`J(5PC1qzy_o!ex)3Zw diff --git a/public/assets/logos/gitea.svg b/public/assets/logos/gitea.svg new file mode 100644 index 0000000..82cad6c --- /dev/null +++ b/public/assets/logos/gitea.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/logos/mastodon.svg b/public/assets/logos/mastodon.svg new file mode 100644 index 0000000..7a2c52c --- /dev/null +++ b/public/assets/logos/mastodon.svg @@ -0,0 +1,6 @@ + + + diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png index b1dcb03f9e627e4223c6aba317944a2202161dcd..abed7ee9ce614b8e9b40742574050e891c98ae42 100644 GIT binary patch delta 190 zcmV;v073uL0q+5jIe(!^L_t(|oTbyj4go<7#_@d(?syGPN5n<;BCd|!WkoCTB!bZ9 zFLsex$qLg+<}!z0zE0bM+kOn&L6Ljl(8KhI>jq?$hq#ouf{ABAG$~Fsk_+)lk=}nG zK2zjCq$y0VxXf{#VRCuC87^zs4Led)eN4^`B4q+k+KDFG2~$D3pRQ*@C+s~a3XrqDUR0p4>`1X+pF Q)&Kwi07*qoM6N<$f`#-*`2YX_ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png index 1cc44630f777b3e319d65636ff5fa52b8fa82d81..2407f5bce83fcbdb368ab0ef75da9f3bef41b9b5 100644 GIT binary patch delta 327 zcmV-N0l5Ct0`~%tIe#fhL_t(|oW<8qO2a@D$MFh!0YSV$7rOKef;+u~^(J1x6L|pt z^*u|$g)ZEPf(t3Q$qQehMJjfZPD7GkAgc`I!*AZqn;F3U9!z!zst0a&2zN@m0^ipl z!Y(Ks!e$Z(ixghw2o;3i6#l+_LinG80zxWAk|6be6E1&@b> z7i$vR3__|F!oT5qI}Ns*%o@pqkObHa*h&NZu3bP>O@W=RVkZZc#n@yDsBcc66yuZS zSU;MthBk|ktUzVxAB4TOU;T(TB9vP(>Slzp9iu*wpgfYHK9r(7mZQFypuC!)zMQJB Z=RZGU#KilQqVoU%002ovPDHLkV1jh#mjnO+ delta 291 zcmV+;0o?xg0@DJJIe!I7L_t(|oXwX#3IZ_@g+IZ$fFRz$LQBse*y$a1a}zJ%xx~go zu+YLr6f8ur5mAIK7D-|@>^e!CN9N<@;{f;@h{}zA6LA8@CG8N{ctE2;GRJ~5i?$CuemGVcL@Zgj18s+X8JLk^5U~J!0@iZqS9Jqq zjs>O`?Z6Yps4Wmr2rs~-q#YHfq1F&)0al)YLlfy2?TaYaya7e~G0L?HC-EUMT7{GN plp3wVNi-%#s~j*XG^eWV`5S3FM*ZDhT44YH002ovPDHLkV1fpmbp`+c diff --git a/public/favicon.ico b/public/favicon.ico index 08f364081786ce045e4ea8186c50fd680f433b27..58c1c93e9c273699495e661fa981ed6406731f67 100644 GIT binary patch literal 5430 zcmc&&O^eh(5UnwKo<$JR(_H*OkN$w!i{MTFg9`qPffYprQ7_`vllUi`yRPnJWnmA> z=tUHSm6dfdnSH)WriZqt6O&9n0!4RMch!4U>90hjC&y%MO;`@(&T*0RA~G1H?U&R) zqV6Jfta3!EXW@OeBdosz&UrfvD0hG}VT@nl46i1@g#so^Lnbpkr9CDwW>x&mIUJ~u zs(uUyvsr|K4pV4qKt=>zQ{BX`8-NR>#EZ}9iwB_Ms0PU8Bo?Kx*ugd++$9C08VE0H?4mQ zoZ!3<`L3RX!0D#(eEvBt&vUk9oLmU)?;)|_f z`I5I5i1}kH!Zov(^W3;vc- zT)SirHhs9Rt|$*5J~0kn1N@R%e(3j&i{)z<#m~xr7~zJCu6J*8i+p%SR}@!$=gmLj zd?SaehQoW{GJWqD9JefkYkX$LZp|q*Wn!-r*NS~{54Ce^*XzbL@zZZ|54W-GdQ-Uni@izQyx7~o^~K&6t{Z#X zxK`|)eSdcS{XOJmqEFeNj40zFg^FoF5nzvh6ZZI5;ZOpKAm;CdxHsw_1>@i-m`W<+ bKy})uV|1+AsIB@?U+PnRD~4hzCY$&R!YaW! literal 5430 zcmc&&O=}cE5Uq90c|#zGIqgLa@#qhjy$IgSe~5wnjD-Y42n6*axq1?R#oon>2f;(a zCKpK{5K%P5n8|zW6cj^uZ_n&}G(5VtyQ<#n>Ymw|5$VZ2SzHwK1KGMS@>oO$gX!;& z7(c++D#l>KV>*t`*M^vQ9R@StKTstPHT=nQRL!Fr7_TU! zr>H@5l+j}~ko!IG2Kbw6xZ)5unHt!-)TN_J z4eHjh5wQp7@Hq|m{lwf3Q04z6bS+#U=Lh_8f-piSl*&t zR)^~x@Qn2guQk8S;8#7dI$ZpGp2wSvvwg;>a)kO=eqa699qfGe{x^d^mY3;6=kE;j z&*_x6r|7={zBQNO)ttVyTaF)a=DGKsyDGfockR37on_B^AMEn}TV?I@u9r2}R%Y#W z@{&Jv=Ix%>&8utQgkgD#c2m5w*4`vo0WZ5OYCS2mH!j&X|WcW~&-t ( export const Default = { args: { icon: , - title: "Worker's cooperatives", + title: "Worker cooperatives", description: "Employee-owned businesses often need to clarify how power is shared, decisions are made, and how processes operate within their organizations.", }, @@ -64,7 +64,7 @@ export const WithLongTitle = { export const WithShortDescription = { args: { icon: , - title: "Worker's cooperatives", + title: "Worker cooperatives", description: "Short description", }, }; diff --git a/stories/navigation/Footer.responsive.stories.js b/stories/navigation/Footer.responsive.stories.js index e1036c1..8d9dbf0 100644 --- a/stories/navigation/Footer.responsive.stories.js +++ b/stories/navigation/Footer.responsive.stories.js @@ -222,10 +222,15 @@ export const Interactive = { }); await userEvent.click(blueskyLink); - const gitlabLink = canvas.getByRole("link", { - name: /follow us on gitlab/i, + const giteaLink = canvas.getByRole("link", { + name: /view source on gitea/i, }); - await userEvent.click(gitlabLink); + await userEvent.click(giteaLink); + + const mastodonLink = canvas.getByRole("link", { + name: /follow us on mastodon/i, + }); + await userEvent.click(mastodonLink); }); }, }; @@ -264,10 +269,15 @@ export const HoverStates = { await userEvent.hover(blueskyLink); await new Promise((resolve) => setTimeout(resolve, 100)); - const gitlabLink = canvas.getByRole("link", { - name: /follow us on gitlab/i, + const giteaLink = canvas.getByRole("link", { + name: /view source on gitea/i, }); - await userEvent.hover(gitlabLink); + await userEvent.hover(giteaLink); + + const mastodonLink = canvas.getByRole("link", { + name: /follow us on mastodon/i, + }); + await userEvent.hover(mastodonLink); await new Promise((resolve) => setTimeout(resolve, 100)); }); }, diff --git a/stories/sections/Groups.stories.js b/stories/sections/Groups.stories.js index 0bac78b..d76951c 100644 --- a/stories/sections/Groups.stories.js +++ b/stories/sections/Groups.stories.js @@ -20,7 +20,7 @@ const sampleItems = [ width={36} /> ), - title: "Worker's cooperatives", + title: "Worker cooperatives", description: "Employee-owned businesses often need to clarify how power is shared, decisions are made, and how processes operate within their organizations.", }, diff --git a/stories/sections/LogoWall.stories.js b/stories/sections/LogoWall.stories.js index d7861e1..c4fd586 100644 --- a/stories/sections/LogoWall.stories.js +++ b/stories/sections/LogoWall.stories.js @@ -13,9 +13,9 @@ export default { - **Mobile**: 3 rows × 2 columns grid with 32px gaps - **SM**: 2 rows × 3 columns grid with 48px row gap and 32px column gap -- **MD**: Single row with space-between layout and 24px gap between text and logos +- **MD+**: Centered flex-wrap row of logos - **LG**: Larger logo sizes and 64px horizontal padding -- **XL**: Largest logo sizes, 160px horizontal padding, and 14px label text +- **XL**: Largest logo sizes and 160px horizontal padding ## Animations & Transitions @@ -28,7 +28,7 @@ export default { ## Props -- **logos** (optional): Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos. +- **logos** (optional): Array of logo objects with src, alt, and size properties. If not provided, uses default partner logos. ## Usage Examples @@ -40,13 +40,11 @@ export default { src: "assets/logos/partners/cu-boulder.svg", alt: "CU Boulder", size: "h-10 lg:h-12 xl:h-[60px]", - order: "order-1 sm:order-2" }, { src: "assets/logos/partners/food-not-bombs.svg", alt: "Food Not Bombs", size: "h-11 lg:h-14 xl:h-[70px]", - order: "order-2 sm:order-1" } ]} /> @@ -65,7 +63,7 @@ This will fall back to the default partner logos.`, logos: { control: "object", description: - "Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.", + "Array of logo objects with src, alt, and size properties. If not provided, uses default partner logos.", }, }, }; diff --git a/tests/components/Footer.test.tsx b/tests/components/Footer.test.tsx index 827d699..3b0b055 100644 --- a/tests/components/Footer.test.tsx +++ b/tests/components/Footer.test.tsx @@ -54,7 +54,10 @@ describe("Footer (behavioral tests)", () => { screen.getAllByRole("link", { name: "Follow us on Bluesky" }).length, ).toBeGreaterThan(0); expect( - screen.getAllByRole("link", { name: "Follow us on GitLab" }).length, + screen.getAllByRole("link", { name: "View source on Gitea" }).length, + ).toBeGreaterThan(0); + expect( + screen.getAllByRole("link", { name: "Follow us on Mastodon" }).length, ).toBeGreaterThan(0); }); @@ -74,7 +77,7 @@ describe("Footer (behavioral tests)", () => { it("renders navigation links with baseline width-fit focus targets", () => { render(
); const useCases = screen.getAllByRole("link", { name: "Use cases" })[0]; - expect(useCases).toHaveClass("w-fit", "self-start"); + expect(useCases).toHaveClass("w-fit", "self-start", "md:self-end"); expect(useCases).not.toHaveClass("w-full"); }); diff --git a/tests/components/Icon.test.tsx b/tests/components/Icon.test.tsx index 94a5322..a419e06 100644 --- a/tests/components/Icon.test.tsx +++ b/tests/components/Icon.test.tsx @@ -12,7 +12,7 @@ type IconProps = React.ComponentProps; const baseProps: IconProps = { icon:
Icon
, - title: "Worker's cooperatives", + title: "Worker cooperatives", description: "Employee-owned businesses often need to clarify how power is shared", }; @@ -89,12 +89,12 @@ describe("Icon (behavioral tests)", () => { render( Icon
} - title="Worker's cooperatives" + title="Worker cooperatives" description="Employee-owned businesses" />, ); expect(screen.getByTestId("icon")).toBeInTheDocument(); - expect(screen.getByText("Worker's cooperatives")).toBeInTheDocument(); + expect(screen.getByText("Worker cooperatives")).toBeInTheDocument(); expect(screen.getByText("Employee-owned businesses")).toBeInTheDocument(); }); diff --git a/tests/components/MultiSelect.test.tsx b/tests/components/MultiSelect.test.tsx index 464ddb0..7696b68 100644 --- a/tests/components/MultiSelect.test.tsx +++ b/tests/components/MultiSelect.test.tsx @@ -166,6 +166,30 @@ describe("MultiSelect – behaviour specifics", () => { expect(handleConfirm).toHaveBeenCalledWith("custom-1", "NewOption"); }); + it("allows spaces in custom chip labels", async () => { + const handleConfirm = vi.fn(); + const customOptions = [ + { id: "custom-1", label: "", state: "custom" as const }, + ]; + render( + , + ); + + const input = screen.getByPlaceholderText("Type to add"); + await userEvent.type(input, "Mutual aid network"); + + const checkButton = screen.getByRole("button", { name: "Confirm" }); + await userEvent.click(checkButton); + + expect(handleConfirm).toHaveBeenCalledWith( + "custom-1", + "Mutual aid network", + ); + }); + it("handles custom chip close", async () => { const handleClose = vi.fn(); const customOptions = [ diff --git a/tests/e2e/critical-journeys.spec.ts b/tests/e2e/critical-journeys.spec.ts index ad7736a..d17fcc3 100644 --- a/tests/e2e/critical-journeys.spec.ts +++ b/tests/e2e/critical-journeys.spec.ts @@ -101,9 +101,7 @@ test.describe("Critical User Journeys", () => { // Check key components are rendered await expect(page.locator('img[alt="Hero illustration"]')).toBeVisible(); - await expect( - page.locator("text=Trusted by leading cooperators"), - ).toBeVisible(); + await expect(page.locator('img[alt="Food Not Bombs"]')).toBeVisible(); await expect(page.locator("text=Jo Freeman")).toBeVisible(); }); diff --git a/tests/pages/learn.test.tsx b/tests/pages/learn.test.tsx index 560a66d..8fac465 100644 --- a/tests/pages/learn.test.tsx +++ b/tests/pages/learn.test.tsx @@ -31,7 +31,7 @@ const mockPosts = [ frontmatter: { title: "Resolving Active Conflicts", description: "Practical steps for resolving conflicts", - author: "Author name", + author: "CommunityRule", date: "2025-04-15", thumbnail: { vertical: "resolving-active-conflicts-vertical.svg", @@ -48,7 +48,7 @@ const mockPosts = [ frontmatter: { title: "Operational Security for Mutual Aid", description: "Tactics to protect members", - author: "Author name", + author: "CommunityRule", date: "2025-04-10", thumbnail: { vertical: "operational-security-mutual-aid-vertical.svg", diff --git a/tests/pages/user-journey.test.jsx b/tests/pages/user-journey.test.jsx index 2b7caba..cdf4164 100644 --- a/tests/pages/user-journey.test.jsx +++ b/tests/pages/user-journey.test.jsx @@ -134,12 +134,16 @@ describe("User Journey Integration", () => { const blueskyLink = screen.getByRole("link", { name: "Follow us on Bluesky", }); - const gitlabLink = screen.getByRole("link", { - name: "Follow us on GitLab", + const giteaLink = screen.getByRole("link", { + name: "View source on Gitea", + }); + const mastodonLink = screen.getByRole("link", { + name: "Follow us on Mastodon", }); expect(blueskyLink).toBeInTheDocument(); - expect(gitlabLink).toBeInTheDocument(); + expect(giteaLink).toBeInTheDocument(); + expect(mastodonLink).toBeInTheDocument(); }); test("user explores features and benefits", async () => { @@ -179,9 +183,11 @@ describe("User Journey Integration", () => { }); const blueskyLink = screen.getByRole("link", { name: /Bluesky/i }); - const gitlabLink = screen.getByRole("link", { name: /GitLab/i }); + const giteaLink = screen.getByRole("link", { name: /Gitea/i }); + const mastodonLink = screen.getByRole("link", { name: /Mastodon/i }); expect(blueskyLink).toBeInTheDocument(); - expect(gitlabLink).toBeInTheDocument(); + expect(giteaLink).toBeInTheDocument(); + expect(mastodonLink).toBeInTheDocument(); }); test("user completes the full journey from discovery to action", async () => { diff --git a/tests/unit/LogoWall.test.jsx b/tests/unit/LogoWall.test.jsx index be366a2..904cdab 100644 --- a/tests/unit/LogoWall.test.jsx +++ b/tests/unit/LogoWall.test.jsx @@ -42,14 +42,6 @@ describe("LogoWall Component", () => { expect(screen.queryByAltText("Food Not Bombs")).not.toBeInTheDocument(); }); - test("renders section label", () => { - render(); - - expect( - screen.getByText("Trusted by leading cooperators"), - ).toBeInTheDocument(); - }); - test("applies correct CSS classes", () => { render(); @@ -74,7 +66,12 @@ describe("LogoWall Component", () => { '[class*="grid grid-cols-2 grid-rows-3"]', ); expect(grid).toBeInTheDocument(); - expect(grid).toHaveClass("sm:grid-cols-3", "sm:grid-rows-2", "md:flex"); + expect(grid).toHaveClass( + "sm:grid-cols-3", + "sm:grid-rows-2", + "md:flex", + "md:justify-center", + ); }); test("renders logos with correct attributes", () => { @@ -88,15 +85,6 @@ describe("LogoWall Component", () => { expect(foodNotBombsLogo).toHaveClass("h-11", "lg:h-14", "xl:h-[70px]"); }); - test("applies order classes for responsive layout", () => { - render(); - - const foodNotBombsContainer = screen - .getByAltText("Food Not Bombs") - .closest("div"); - expect(foodNotBombsContainer).toHaveClass("order-1", "sm:order-4"); - }); - test("handles empty logos array", () => { render(); @@ -119,9 +107,7 @@ describe("LogoWall Component", () => { const section = document.querySelector("section"); expect(section).toBeInTheDocument(); - // Check for the label - const label = screen.getByText("Trusted by leading cooperators"); - expect(label).toBeInTheDocument(); + expect(screen.queryByText("Trusted by leading cooperators")).not.toBeInTheDocument(); }); test("applies transition effects", () => {