[SM-956] Secret Manager: Integrations Page (#8701)

* add navigation item for integrations and SDKs page

* Initial routing to Integrations & SDKs page

* Initial add of integrations component

* Initial add of SDKs component

* add secret manage integration images

* remove integration & sdk components in favor of a single component

* add integration & integration grid components

* add integrations & sdks

* rename page & components to integration after design discussion

* add external rel attribute for SDK links

* remove ts extension

* refactor: use pseudo element to cover as a link

* refactor: change secondaryText to linkText to align with usage

* update icon for integrations

* add new badge option for integration cards

* hardcode integration/sdk names

* add dark mode images for integrations and sdks

* update integration/sdk card with dark mode image when applicable

* refactor integration types to be an enum

* fix enum typings in integration grid test

---------

Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
This commit is contained in:
Nick Krantz 2024-04-18 14:24:16 -05:00 committed by GitHub
parent ce75f7b565
commit 40ba15c07e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 841 additions and 0 deletions

View File

@ -0,0 +1,4 @@
<svg width="112" height="111" viewBox="0 0 112 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M96.7901 56.5804C96.7901 64.3954 94.4726 72.0349 90.1309 78.5328C85.7891 85.0308 79.6179 90.0953 72.3978 93.086C65.1777 96.0767 57.2329 96.8592 49.568 95.3345C41.9032 93.8099 34.8626 90.0466 29.3365 84.5206C23.8105 78.9945 20.0472 71.9539 18.5226 64.2891C16.9979 56.6242 17.7804 48.6794 20.7711 41.4593C23.7618 34.2391 28.8263 28.068 35.3242 23.7262C41.8222 19.3844 49.4617 17.067 57.2767 17.067C62.4657 17.0667 67.604 18.0886 72.3981 20.0742C77.1922 22.0599 81.5483 24.9704 85.2175 28.6396C88.8867 32.3088 91.7972 36.6648 93.7829 41.4589C95.7685 46.253 96.7903 51.3913 96.7901 56.5804Z" fill="#FF5850"/>
<path d="M57.9424 41.7704L68.1686 67.0076L52.7229 54.8419L57.9424 41.7704ZM76.1078 72.8215L60.379 34.9703C60.1981 34.4723 59.8658 34.0436 59.4288 33.744C58.9919 33.4445 58.4721 33.2893 57.9424 33.3002C57.4042 33.288 56.8752 33.4417 56.4274 33.7405C55.9795 34.0392 55.6345 34.4686 55.4391 34.9703L38.1761 76.4888H44.0818L50.9147 59.3696L71.3083 75.8457C72.1292 76.5092 72.7202 76.8092 73.4901 76.8092C73.8662 76.8181 74.2403 76.7516 74.5903 76.6137C74.9404 76.4757 75.2593 76.2692 75.5283 76.0062C75.7974 75.7432 76.0111 75.429 76.1568 75.0822C76.3026 74.7353 76.3776 74.3628 76.3772 73.9866C76.3505 73.5863 76.2595 73.1929 76.1078 72.8215Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,10 @@
<svg width="115" height="111" viewBox="0 0 115 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_212_1335)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M57.3312 0.0012207C26.0958 0.0012207 0.845001 25.4382 0.845001 56.9071C0.845001 82.0619 17.024 103.355 39.4687 110.891C42.2748 111.458 43.3027 109.667 43.3027 108.16C43.3027 106.841 43.2102 102.319 43.2102 97.6074C27.4971 101 24.225 90.8238 24.225 90.8238C21.6998 84.2287 17.9583 82.5337 17.9583 82.5337C12.8154 79.0477 18.3329 79.0477 18.3329 79.0477C24.0377 79.4246 27.0312 84.8889 27.0312 84.8889C32.0804 93.556 40.2168 91.1071 43.49 89.5994C43.9571 85.9249 45.4545 83.3812 47.0443 81.9683C34.5119 80.649 21.3264 75.7501 21.3264 53.8917C21.3264 47.6735 23.5694 42.5861 27.1237 38.6295C26.5629 37.2166 24.5985 31.3742 27.6856 23.5547C27.6856 23.5547 32.455 22.047 43.2091 29.3959C47.8133 28.1503 52.5615 27.5166 57.3312 27.5113C62.1006 27.5113 66.9625 28.1715 71.4522 29.3959C82.2074 22.047 86.9768 23.5547 86.9768 23.5547C90.0639 31.3742 88.0983 37.2166 87.5376 38.6295C91.1854 42.5861 93.336 47.6735 93.336 53.8917C93.336 75.7501 80.1504 80.5542 67.5245 81.9683C69.5825 83.7581 71.3585 87.1493 71.3585 92.52C71.3585 100.151 71.266 106.276 71.266 108.159C71.266 109.667 72.2951 111.458 75.1001 110.892C97.5447 103.354 113.724 82.0619 113.724 56.9071C113.816 25.4382 88.4729 0.0012207 57.3312 0.0012207Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_212_1335">
<rect width="113.31" height="110.998" fill="white" transform="translate(0.845001 0.0012207)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="114" height="111" viewBox="0 0 114 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M57.3312 0C26.0951 0 0.84375 25.4375 0.84375 56.9072C0.84375 82.0625 17.0232 103.356 39.4683 110.892C42.2745 111.459 43.3024 109.668 43.3024 108.161C43.3024 106.842 43.2099 102.32 43.2099 97.6083C27.4965 101.001 24.2243 90.8246 24.2243 90.8246C21.699 84.2293 17.9574 82.5343 17.9574 82.5343C12.8144 79.0482 18.332 79.0482 18.332 79.0482C24.037 79.4251 27.0305 84.8895 27.0305 84.8895C32.0798 93.5568 40.2164 91.1079 43.4897 89.6001C43.9568 85.9256 45.4542 83.3818 47.044 81.9689C34.5114 80.6496 21.3256 75.7506 21.3256 53.8917C21.3256 47.6733 23.5687 42.5858 27.123 38.6292C26.5622 37.2162 24.5978 31.3737 27.6849 23.554C27.6849 23.554 32.4545 22.0462 43.2087 29.3953C47.813 28.1497 52.5614 27.516 57.3312 27.5107C62.1007 27.5107 66.9628 28.1709 71.4525 29.3953C82.2079 22.0462 86.9774 23.554 86.9774 23.554C90.0646 31.3737 88.099 37.2162 87.5382 38.6292C91.1862 42.5858 93.3368 47.6733 93.3368 53.8917C93.3368 75.7506 80.1509 80.5548 67.5247 81.9689C69.5828 83.7587 71.3588 87.15 71.3588 92.5208C71.3588 100.152 71.2663 106.277 71.2663 108.16C71.2663 109.668 72.2954 111.459 75.1004 110.894C97.5456 103.355 113.725 82.0625 113.725 56.9072C113.817 25.4375 88.4736 0 57.3312 0Z" fill="#24292F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,3 @@
<svg width="57" height="55" viewBox="0 0 57 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M55.9495 21.8712L55.8706 21.6696L48.235 1.7422C48.0796 1.35164 47.8045 1.02032 47.4492 0.795784C47.0937 0.575065 46.6789 0.46876 46.261 0.491221C45.8431 0.513683 45.4422 0.66383 45.1124 0.92139C44.7862 1.18634 44.5495 1.54537 44.4347 1.9496L39.279 17.7233H18.4023L13.2466 1.9496C13.1348 1.54316 12.8976 1.18234 12.5689 0.918468C12.2391 0.660908 11.8381 0.510761 11.4202 0.4883C11.0024 0.465838 10.5876 0.572144 10.2321 0.792863C9.87754 1.0183 9.6027 1.34933 9.44631 1.73928L1.79608 21.6579L1.72013 21.8595C0.620944 24.7315 0.485268 27.883 1.33356 30.8388C2.18184 33.7947 3.96811 36.3946 6.42302 38.2466L6.44931 38.267L6.51942 38.3167L18.151 47.0273L23.9055 51.3826L27.4108 54.029C27.8208 54.3404 28.3215 54.5089 28.8363 54.5089C29.3511 54.5089 29.8517 54.3404 30.2617 54.029L33.767 51.3826L39.5215 47.0273L51.2232 38.2641L51.2524 38.2408C53.7018 36.3884 55.4839 33.7912 56.3309 30.8393C57.1779 27.8875 57.0441 24.7404 55.9495 21.8712Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,6 @@
<svg width="112" height="111" viewBox="0 0 112 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M82.9495 49.8712L82.8706 49.6696L75.235 29.7422C75.0796 29.3516 74.8045 29.0203 74.4492 28.7958C74.0937 28.5751 73.6789 28.4688 73.261 28.4912C72.8431 28.5137 72.4422 28.6638 72.1124 28.9214C71.7862 29.1863 71.5495 29.5454 71.4347 29.9496L66.279 45.7233H45.4023L40.2466 29.9496C40.1348 29.5432 39.8976 29.1823 39.5689 28.9185C39.2391 28.6609 38.8381 28.5108 38.4202 28.4883C38.0024 28.4658 37.5876 28.5721 37.2321 28.7929C36.8775 29.0183 36.6027 29.3493 36.4463 29.7393L28.7961 49.6579L28.7201 49.8595C27.6209 52.7315 27.4853 55.883 28.3336 58.8388C29.1818 61.7947 30.9681 64.3946 33.423 66.2466L33.4493 66.267L33.5194 66.3167L45.151 75.0273L50.9055 79.3826L54.4108 82.029C54.8208 82.3404 55.3215 82.5089 55.8363 82.5089C56.3511 82.5089 56.8517 82.3404 57.2617 82.029L60.767 79.3826L66.5215 75.0273L78.2232 66.2641L78.2524 66.2408C80.7018 64.3884 82.4839 61.7912 83.3309 58.8393C84.1779 55.8875 84.0441 52.7404 82.9495 49.8712Z" fill="#E24329"/>
<path d="M82.9495 49.8712L82.8706 49.6697C79.15 50.4334 75.6441 52.0093 72.6031 54.2849L55.8333 66.9652C61.544 71.2855 66.5156 75.039 66.5156 75.039L78.2174 66.2759L78.2466 66.2525C80.6995 64.4002 82.4844 61.8013 83.3325 58.8469C84.1807 55.8925 84.0463 52.7426 82.9495 49.8712Z" fill="#FC6D26"/>
<path d="M45.151 75.039L50.9055 79.3943L54.4108 82.0408C54.8208 82.3521 55.3215 82.5206 55.8363 82.5206C56.3511 82.5206 56.8517 82.3521 57.2617 82.0408L60.767 79.3943L66.5215 75.039C66.5215 75.039 61.544 71.2738 55.8333 66.9652C50.1227 71.2738 45.151 75.039 45.151 75.039Z" fill="#FCA326"/>
<path d="M39.0607 54.2849C36.0222 52.0046 32.5169 50.4245 28.7961 49.658L28.7201 49.8595C27.6209 52.7315 27.4853 55.883 28.3336 58.8388C29.1818 61.7947 30.9681 64.3946 33.423 66.2466L33.4493 66.2671L33.5194 66.3167L45.151 75.0273C45.151 75.0273 50.1168 71.2738 55.8333 66.9535L39.0607 54.2849Z" fill="#FC6D26"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,7 @@
<svg width="99" height="111" viewBox="0 0 99 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M98.6672 32.5494C98.6665 30.6878 98.2683 29.0428 97.4632 27.6311C96.6723 26.2428 95.4879 25.0792 93.8992 24.1592C80.7854 16.5977 67.6589 9.05936 54.5493 1.49017C51.0151 -0.55023 47.5883 -0.475845 44.0803 1.59384C38.8606 4.67217 12.7274 19.6444 4.94006 24.155C1.73301 26.0115 0.172453 28.8528 0.171682 32.5459C0.166672 47.7525 0.171682 62.9587 0.166672 78.1657C0.166672 79.9864 0.54862 81.5994 1.3183 82.99C2.10956 84.4203 3.31052 85.6158 4.93582 86.5566C12.7235 91.0672 38.8602 106.038 44.0787 109.117C47.5883 111.188 51.0151 111.262 54.5505 109.221C67.6605 101.651 80.7877 94.1135 93.9035 86.552C95.5288 85.6116 96.7297 84.4153 97.521 82.9861C98.2895 81.5955 98.6722 79.9826 98.6722 78.1615C98.6722 78.1615 98.6722 47.7564 98.6672 32.5494Z" fill="#A179DC"/>
<path d="M49.5702 55.2075L1.3183 82.9899C2.10956 84.4202 3.31052 85.6157 4.93582 86.5565C12.7235 91.0671 38.8602 106.038 44.0787 109.117C47.5883 111.188 51.0151 111.262 54.5505 109.221C67.6605 101.651 80.7878 94.1134 93.9035 86.5519C95.5288 85.6115 96.7297 84.4152 97.521 82.986L49.5702 55.2075Z" fill="#280068"/>
<path d="M98.6672 32.5494C98.6665 30.6878 98.2683 29.0429 97.4632 27.6311L49.5702 55.2077L97.521 82.9862C98.2895 81.5956 98.6715 79.9826 98.6722 78.1615C98.6722 78.1615 98.6722 47.7564 98.6672 32.5494Z" fill="#390091"/>
<path d="M77.9793 44.8215V50.0145H83.1723V44.8215H85.7688V50.0145H90.9618V52.611H85.7688V57.804H90.9618V60.4005H85.7688V65.5935H83.1723V60.4005H77.9793V65.5935H75.3828V60.4005H70.1898V57.804H75.3828V52.611H70.1898V50.0145H75.3828V44.8215H77.9793ZM83.1723 52.611H77.9793V57.804H83.1723V52.611Z" fill="white"/>
<path d="M49.676 18.7412C63.221 18.7412 75.0469 26.0974 81.3811 37.0316L81.3193 36.9263L65.3829 46.1024C62.243 40.7861 56.4869 37.1985 49.884 37.1246L49.676 37.1235C39.6068 37.1235 31.4435 45.2863 31.4435 55.3556C31.4435 58.6484 32.3214 61.7351 33.8481 64.4011C36.9908 69.8876 42.8988 73.5881 49.676 73.5881C56.4951 73.5881 62.4368 69.8406 65.5635 64.2954L65.4875 64.4285L81.4 73.6469C75.1353 84.4885 63.4715 91.822 50.0839 91.9682L49.676 91.9704C36.0883 91.9704 24.2288 84.5689 17.9106 73.5769C14.8261 68.2108 13.0612 61.9896 13.0612 55.3556C13.0612 35.1343 29.4539 18.7412 49.676 18.7412Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,7 @@
<svg width="207" height="77" viewBox="0 0 207 77" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.3 23.3499C15.9 23.3499 15.8 23.1499 16 22.8499L18.1 20.1499C18.3 19.8499 18.8 19.6499 19.2 19.6499H54.9C55.3 19.6499 55.4 19.9499 55.2 20.2499L53.5 22.8499C53.3 23.1499 52.8 23.4499 52.5 23.4499L16.3 23.3499Z" fill="#00ACD7"/>
<path d="M1.2 32.5499C0.800003 32.5499 0.700003 32.3499 0.900003 32.0499L3 29.3499C3.2 29.0499 3.7 28.8499 4.1 28.8499H49.7C50.1 28.8499 50.3 29.1499 50.2 29.4499L49.4 31.8499C49.3 32.2499 48.9 32.4499 48.5 32.4499L1.2 32.5499Z" fill="#00ACD7"/>
<path d="M25.4 41.7498C25 41.7498 24.9 41.4498 25.1 41.1498L26.5 38.6498C26.7 38.3498 27.1 38.0498 27.5 38.0498H47.5C47.9 38.0498 48.1 38.3498 48.1 38.7498L47.9 41.1498C47.9 41.5498 47.5 41.8498 47.2 41.8498L25.4 41.7498Z" fill="#00ACD7"/>
<path d="M129.2 21.5498C122.9 23.1498 118.6 24.3498 112.4 25.9498C110.9 26.3498 110.8 26.4498 109.5 24.9498C108 23.2498 106.9 22.1498 104.8 21.1498C98.5 18.0498 92.4 18.9498 86.7 22.6498C79.9 27.0498 76.4 33.5498 76.5 41.6498C76.6 49.6498 82.1 56.2498 90 57.3498C96.8 58.2498 102.5 55.8498 107 50.7498C107.9 49.6498 108.7 48.4498 109.7 47.0498C106.1 47.0498 101.6 47.0498 90.4 47.0498C88.3 47.0498 87.8 45.7498 88.5 44.0498C89.8 40.9498 92.2 35.7498 93.6 33.1498C93.9 32.5498 94.6 31.5498 96.1 31.5498C101.2 31.5498 120 31.5498 132.5 31.5498C132.3 34.2498 132.3 36.9498 131.9 39.6498C130.8 46.8498 128.1 53.4498 123.7 59.2498C116.5 68.7498 107.1 74.6498 95.2 76.2498C85.4 77.5498 76.3 75.6498 68.3 69.6498C60.9 64.0498 56.7 56.6498 55.6 47.4498C54.3 36.5498 57.5 26.7498 64.1 18.1498C71.2 8.8498 80.6 2.94981 92.1 0.849805C101.5 -0.850195 110.5 0.249805 118.6 5.7498C123.9 9.24981 127.7 14.0498 130.2 19.8498C130.8 20.7498 130.4 21.2498 129.2 21.5498Z" fill="#00ACD7"/>
<path d="M162.3 76.8498C153.2 76.6498 144.9 74.0499 137.9 68.0499C132 62.9499 128.3 56.4498 127.1 48.7498C125.3 37.4498 128.4 27.4498 135.2 18.5498C142.5 8.94985 151.3 3.94985 163.2 1.84985C173.4 0.0498471 183 1.04985 191.7 6.94985C199.6 12.3498 204.5 19.6498 205.8 29.2498C207.5 42.7498 203.6 53.7498 194.3 63.1498C187.7 69.8498 179.6 74.0499 170.3 75.9499C167.6 76.4499 164.9 76.5498 162.3 76.8498ZM186.1 36.4498C186 35.1498 186 34.1498 185.8 33.1498C184 23.2498 174.9 17.6498 165.4 19.8498C156.1 21.9498 150.1 27.8498 147.9 37.2498C146.1 45.0498 149.9 52.9498 157.1 56.1498C162.6 58.5498 168.1 58.2498 173.4 55.5498C181.3 51.4498 185.6 45.0498 186.1 36.4498Z" fill="#00ACD7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,15 @@
<svg width="61" height="111" viewBox="0 0 61 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3909 58.8794C20.3909 58.8794 17.4831 60.5719 22.4627 61.143C28.4963 61.8318 31.5794 61.7326 38.2264 60.4762C38.2264 60.4762 39.9775 61.5716 42.4188 62.5204C27.5132 68.9065 8.68435 62.1503 20.3909 58.8794Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5686 50.5444C18.5686 50.5444 15.3067 52.9595 20.29 53.4745C26.7358 54.1399 31.8241 54.1945 40.6342 52.499C40.6342 52.499 41.8499 53.7339 43.7643 54.4087C25.7454 59.6794 5.67598 54.8234 18.5686 50.5444Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.9225 36.4037C37.5972 40.6336 32.959 44.437 32.959 44.437C32.959 44.437 42.2841 39.6239 38.0024 33.5939C34.002 27.9731 30.935 25.181 47.539 15.5518C47.539 15.5518 21.4749 22.0598 33.9225 36.4037Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.6361 65.0451C53.6361 65.0451 55.7887 66.8196 51.2652 68.1919C42.6637 70.7972 15.4578 71.5831 7.90133 68.2962C5.18686 67.1138 10.2796 65.4751 11.882 65.129C13.5528 64.7675 14.5067 64.8336 14.5067 64.8336C11.4857 62.7062 -5.0194 69.0116 6.12397 70.82C36.5124 75.7457 61.5174 68.6013 53.6361 65.0451Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7904 41.9075C21.7904 41.9075 7.95357 45.1949 16.8906 46.3897C20.6653 46.8948 28.1855 46.7786 35.1956 46.1905C40.923 45.7098 46.6708 44.6817 46.6708 44.6817C46.6708 44.6817 44.6528 45.5474 43.1917 46.5445C29.1378 50.2409 1.99342 48.5194 9.80908 44.7404C16.4158 41.5442 21.7904 41.9075 21.7904 41.9075Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.6135 55.7815C60.8988 48.3603 54.2931 41.2277 49.6827 42.1884C48.5553 42.4237 48.0495 42.6275 48.0495 42.6275C48.0495 42.6275 48.4696 41.9695 49.2694 41.6864C58.3903 38.4813 65.4033 51.1426 46.3281 56.1571C46.3281 56.1571 46.5474 55.9584 46.6135 55.7815Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.0026 0C38.0026 0 45.9122 7.9142 30.4982 20.0806C18.1371 29.8439 27.6798 35.4086 30.4938 41.7697C23.2777 35.2597 17.9848 29.5281 21.535 24.1945C26.7484 16.3663 41.1932 12.5697 38.0026 0Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.1948 75.8976C36.904 76.7741 57.9617 75.4098 58.4601 68.9224C58.4601 68.9224 57.5021 71.3817 47.1293 73.333C35.4271 75.5362 20.9905 75.2797 12.4314 73.8664C12.4314 73.8664 14.1849 75.3177 23.1948 75.8976Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M57.8271 87.5323H57.4096V87.299H58.534V87.5323H58.1182V88.6991H57.827V87.5323H57.8271ZM60.0722 87.5914H60.0668L59.6523 88.6989H59.4617L59.0497 87.5914H59.0455V88.6989H58.7696V87.2988H59.1742L59.5555 88.29L59.938 87.2988H60.3402V88.6989H60.0724L60.0722 87.5914Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9484 100.686C19.6556 101.806 18.2885 102.436 17.0614 102.436C15.3135 102.436 14.3654 101.386 14.3654 99.7053C14.3654 97.8859 15.3805 96.5541 19.4456 96.5541H20.9484V100.686ZM24.5171 104.711V92.2487C24.5171 89.063 22.7003 86.9609 18.3222 86.9609C15.7675 86.9609 13.5277 87.5925 11.7069 88.3967L12.2308 90.6041C13.6649 90.0774 15.5194 89.5883 17.3403 89.5883C19.8629 89.5883 20.9484 90.6041 20.9484 92.705V94.2808H19.6877C13.56 94.2808 10.7953 96.6572 10.7953 100.232C10.7953 103.312 12.6175 105.063 16.0476 105.063C18.2519 105.063 19.8995 104.153 21.4373 102.82L21.7174 104.711H24.5171Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.4267 104.711H31.9764L26.6194 87.2793H30.5061L33.8313 97.991L34.5708 101.209C36.2491 96.5543 37.4394 91.8281 38.0346 87.2793H41.8148C40.804 93.0192 38.9801 99.3202 36.4267 104.711Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.5036 100.686C52.2065 101.806 50.8391 102.436 49.6154 102.436C47.8668 102.436 46.9187 101.386 46.9187 99.7053C46.9187 97.8859 47.9358 96.5541 51.9974 96.5541H53.5034V100.686H53.5036ZM57.0735 104.711V92.2487C57.0735 89.063 55.2515 86.9609 50.8773 86.9609C48.3193 86.9609 46.08 87.5925 44.2592 88.3967L44.784 90.6041C46.2188 90.0774 48.0758 89.5883 49.8965 89.5883C52.4161 89.5883 53.5034 90.6041 53.5034 92.705V94.2808H52.2428C46.1137 94.2808 43.3499 96.6572 43.3499 100.232C43.3499 103.312 45.1695 105.063 48.5994 105.063C50.8056 105.063 52.4499 104.153 53.992 102.82L54.2739 104.711H57.0735Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.88172 107.674C5.86495 109.16 4.22063 110.337 2.42204 111L0.660004 108.924C2.03012 108.221 3.2037 107.087 3.74972 106.031C4.22063 105.089 4.41668 103.879 4.41668 100.983V81.0786H8.2094V100.708C8.2094 104.582 7.90017 106.149 6.88172 107.674Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,15 @@
<svg width="61" height="111" viewBox="0 0 61 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3909 58.8794C20.3909 58.8794 17.4831 60.5719 22.4627 61.143C28.4963 61.8318 31.5794 61.7326 38.2264 60.4762C38.2264 60.4762 39.9775 61.5716 42.4188 62.5204C27.5132 68.9065 8.68435 62.1503 20.3909 58.8794Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5686 50.5444C18.5686 50.5444 15.3067 52.9595 20.29 53.4745C26.7358 54.1399 31.8241 54.1945 40.6342 52.499C40.6342 52.499 41.8499 53.7339 43.7643 54.4087C25.7454 59.6794 5.67598 54.8234 18.5686 50.5444Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.9225 36.4037C37.5972 40.6336 32.959 44.437 32.959 44.437C32.959 44.437 42.2841 39.6239 38.0024 33.5939C34.002 27.9731 30.935 25.181 47.539 15.5518C47.539 15.5518 21.4749 22.0598 33.9225 36.4037Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.6361 65.0453C53.6361 65.0453 55.7887 66.8198 51.2652 68.1921C42.6637 70.7975 15.4578 71.5833 7.90133 68.2964C5.18686 67.1141 10.2796 65.4753 11.882 65.1292C13.5528 64.7678 14.5067 64.8338 14.5067 64.8338C11.4857 62.7064 -5.0194 69.0119 6.12397 70.8202C36.5124 75.7459 61.5174 68.6016 53.6361 65.0453Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7904 41.9072C21.7904 41.9072 7.95357 45.1947 16.8906 46.3895C20.6653 46.8946 28.1855 46.7784 35.1956 46.1902C40.923 45.7096 46.6708 44.6815 46.6708 44.6815C46.6708 44.6815 44.6528 45.5472 43.1917 46.5443C29.1378 50.2406 1.99342 48.5192 9.80908 44.7401C16.4158 41.5439 21.7904 41.9072 21.7904 41.9072Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.6135 55.7813C60.8988 48.36 54.2931 41.2275 49.6827 42.1882C48.5553 42.4235 48.0495 42.6273 48.0495 42.6273C48.0495 42.6273 48.4696 41.9692 49.2694 41.6861C58.3903 38.481 65.4033 51.1424 46.3281 56.1569C46.3281 56.1569 46.5474 55.9581 46.6135 55.7813Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.0026 0C38.0026 0 45.9122 7.9142 30.4982 20.0806C18.1371 29.8439 27.6798 35.4086 30.4938 41.7697C23.2777 35.2597 17.9848 29.5281 21.535 24.1945C26.7484 16.3663 41.1932 12.5697 38.0026 0Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.1948 75.8976C36.904 76.7741 57.9617 75.4098 58.4601 68.9224C58.4601 68.9224 57.5021 71.3817 47.1293 73.333C35.4271 75.5362 20.9905 75.2797 12.4314 73.8664C12.4314 73.8664 14.1849 75.3177 23.1948 75.8976Z" fill="#0074BD"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M57.8271 87.532H57.4096V87.2987H58.534V87.532H58.1182V88.6988H57.827V87.532H57.8271ZM60.0722 87.5911H60.0668L59.6523 88.6987H59.4617L59.0497 87.5911H59.0455V88.6987H58.7696V87.2986H59.1742L59.5555 88.2898L59.938 87.2986H60.3402V88.6987H60.0724L60.0722 87.5911Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9484 100.686C19.6556 101.806 18.2885 102.436 17.0614 102.436C15.3135 102.436 14.3654 101.386 14.3654 99.7053C14.3654 97.8859 15.3805 96.5541 19.4456 96.5541H20.9484V100.686ZM24.5171 104.711V92.2487C24.5171 89.063 22.7003 86.9609 18.3222 86.9609C15.7675 86.9609 13.5277 87.5925 11.7069 88.3967L12.2308 90.6041C13.6649 90.0774 15.5194 89.5883 17.3403 89.5883C19.8629 89.5883 20.9484 90.6041 20.9484 92.705V94.2808H19.6877C13.56 94.2808 10.7953 96.6572 10.7953 100.232C10.7953 103.312 12.6175 105.063 16.0476 105.063C18.2519 105.063 19.8995 104.153 21.4373 102.82L21.7174 104.711H24.5171Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.4267 104.711H31.9764L26.6194 87.2791H30.5061L33.8313 97.9908L34.5708 101.209C36.2491 96.5541 37.4394 91.8278 38.0346 87.2791H41.8148C40.804 93.0189 38.9801 99.32 36.4267 104.711Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.5036 100.686C52.2065 101.806 50.8391 102.436 49.6154 102.436C47.8668 102.436 46.9187 101.386 46.9187 99.7053C46.9187 97.8859 47.9358 96.5541 51.9974 96.5541H53.5034V100.686H53.5036ZM57.0735 104.711V92.2487C57.0735 89.063 55.2515 86.9609 50.8773 86.9609C48.3193 86.9609 46.08 87.5925 44.2592 88.3967L44.784 90.6041C46.2188 90.0774 48.0758 89.5883 49.8965 89.5883C52.4161 89.5883 53.5034 90.6041 53.5034 92.705V94.2808H52.2428C46.1137 94.2808 43.3499 96.6572 43.3499 100.232C43.3499 103.312 45.1695 105.063 48.5994 105.063C50.8056 105.063 52.4499 104.153 53.992 102.82L54.2739 104.711H57.0735Z" fill="#EA2D2E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.88172 107.674C5.86495 109.159 4.22063 110.336 2.42204 111L0.660004 108.924C2.03012 108.221 3.2037 107.086 3.74972 106.03C4.22063 105.089 4.41668 103.879 4.41668 100.983V81.0784H8.2094V100.708C8.2094 104.582 7.90017 106.148 6.88172 107.674Z" fill="#EA2D2E"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,4 @@
<svg width="191" height="100" viewBox="0 0 191 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M95.5 99.9169C148.187 99.9169 190.899 77.6617 190.899 50.2085C190.899 22.7552 148.187 0.5 95.5 0.5C42.8127 0.5 0.101196 22.7552 0.101196 50.2085C0.101196 77.6617 42.8127 99.9169 95.5 99.9169Z" fill="#8993BE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.8911 79.6411L37.3462 26.662H61.5216C71.976 27.3163 77.2035 32.5483 77.2035 41.7059C77.2035 57.4032 64.789 66.5601 53.6803 65.9058H41.9201L39.3064 79.6411H26.8911ZM44.5332 56.0954L47.8006 36.4732H56.2948C60.8687 36.4732 64.1354 38.4352 64.1354 42.3594C63.4825 53.4791 58.255 55.4412 52.3745 56.0954H44.5332ZM74.7746 65.9058L85.229 12.9275H97.6442L95.0304 26.662H106.791C117.246 27.3163 121.167 32.5483 119.86 39.0895L115.286 65.9058H102.218L106.792 41.7059C107.445 38.4352 107.445 36.4732 102.872 36.4732H93.0703L87.1898 65.9058H74.7746ZM114.449 79.6411L124.904 26.662H149.08C159.535 27.3163 164.762 32.5483 164.762 41.7059C164.762 57.4032 152.347 66.5601 141.239 65.9058H129.478L126.864 79.6411H114.449ZM132.091 56.0954L135.358 36.4732H143.853C148.427 36.4732 151.694 38.4352 151.694 42.3594C151.041 53.4791 145.813 55.4412 139.933 56.0954H132.091H132.091Z" fill="#232531"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,19 @@
<svg width="93" height="111" viewBox="0 0 93 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M46.0221 0.000755848C42.2534 0.0182675 38.6543 0.33969 35.4874 0.900061C26.1583 2.54821 24.4645 5.99792 24.4645 12.3598V20.7619H46.5103V23.5626H24.4645H16.1909C9.78376 23.5626 4.17349 27.4136 2.41867 34.7396C0.39451 43.137 0.304727 48.377 2.41867 57.1452C3.98577 63.6719 7.72821 68.3223 14.1353 68.3223H21.7152V58.2501C21.7152 50.9735 28.0111 44.5549 35.4874 44.5549H57.5076C63.6372 44.5549 68.5305 39.508 68.5305 33.3521V12.3598C68.5305 6.38524 63.4903 1.89719 57.5076 0.900061C53.7204 0.269643 49.7909 -0.0167561 46.0221 0.000755848ZM34.0999 6.75839C36.3771 6.75839 38.2367 8.64839 38.2367 10.9723C38.2367 13.2879 36.3771 15.1605 34.0999 15.1605C31.8146 15.1605 29.9631 13.2879 29.9631 10.9723C29.9631 8.64839 31.8146 6.75839 34.0999 6.75839Z" fill="url(#paint0_linear_91_2213)"/>
<path d="M71.2798 23.5625V33.3521C71.2798 40.9418 64.8451 47.3299 57.5075 47.3299H35.4874C29.4557 47.3299 24.4645 52.4922 24.4645 58.5326V79.525C24.4645 85.4995 29.6598 89.0137 35.4874 90.7278C42.4659 92.7797 49.1579 93.1506 57.5075 90.7278C63.0577 89.1208 68.5305 85.8869 68.5304 79.525V71.1229H46.5103V68.3222H68.5304H79.5534C85.9605 68.3222 88.3481 63.8531 90.5763 57.1451C92.878 50.2394 92.78 43.5984 90.5763 34.7396C88.9929 28.3612 85.9687 23.5625 79.5534 23.5625H71.2798ZM58.895 76.7243C61.1804 76.7243 63.0318 78.5969 63.0318 80.9125C63.0318 83.2364 61.1804 85.1264 58.895 85.1264C56.6178 85.1264 54.7582 83.2364 54.7582 80.9125C54.7582 78.5969 56.6178 76.7243 58.895 76.7243Z" fill="url(#paint1_linear_91_2213)"/>
<path opacity="0.44382" d="M46.7605 110.727C63.0768 110.727 76.3037 108.162 76.3037 104.998C76.3037 101.835 63.0768 99.2698 46.7605 99.2698C30.4442 99.2698 17.2172 101.835 17.2172 104.998C17.2172 108.162 30.4442 110.727 46.7605 110.727Z" fill="url(#paint2_radial_91_2213)"/>
<defs>
<linearGradient id="paint0_linear_91_2213" x1="0.866699" y1="-2.38577e-07" x2="51.702" y2="43.3187" gradientUnits="userSpaceOnUse">
<stop stop-color="#5A9FD4"/>
<stop offset="1" stop-color="#306998"/>
</linearGradient>
<linearGradient id="paint1_linear_91_2213" x1="58.3652" y1="80.2061" x2="40.1275" y2="54.6496" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD43B"/>
<stop offset="1" stop-color="#FFE873"/>
</linearGradient>
<radialGradient id="paint2_radial_91_2213" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(46.7605 104.998) rotate(-90) scale(5.72869 25.18)">
<stop stop-color="#B8B8B8" stop-opacity="0.498039"/>
<stop offset="1" stop-color="#7F7F7F" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,11 @@
<svg width="112" height="111" viewBox="0 0 112 111" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_91_2175)">
<path d="M68.5294 0C68.5294 0.195882 68.5294 0.391765 68.5294 0.598529C68.5294 7.62853 62.8307 13.3254 55.8025 13.3254C48.7725 13.3254 43.0756 7.62672 43.0756 0.598529C43.0756 0.391765 43.0756 0.195882 43.0756 0L0.333344 0V111H111.333V0H68.5294Z" fill="#654FF0"/>
<path d="M26.1173 59.8184H33.4737L38.4959 86.5654H38.5866L44.6227 59.8184H51.504L56.956 86.8937H57.063L62.7872 59.8184H70.0022L60.627 99.12H53.3268L47.9201 72.373H47.7786L41.991 99.12H34.5547L26.1173 59.8184ZM78.2963 59.8184H89.8933L101.41 99.12H93.8218L91.3171 90.3742H78.1059L76.1725 99.12H68.7815L78.2963 59.8184ZM82.7109 69.5055L79.5025 83.9246H89.4888L85.8052 69.5055H82.7109Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_91_2175">
<rect width="111" height="111" fill="white" transform="translate(0.333344)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 975 B

View File

@ -7906,6 +7906,9 @@
"restrictedGroupAccessDesc": {
"message": "You cannot add yourself to a group."
},
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerNotice": {
"message": "Notice: Unassigned organization items are no longer visible in your All Vaults view across devices and are now only accessible via the Admin Console."
},
@ -7962,6 +7965,55 @@
"errorAssigningTargetFolder": {
"message": "Error assigning target folder."
},
"integrationsAndSdks": {
"message": "Integrations & SDKs",
"description": "The title for the section that deals with integrations and SDKs."
},
"integrations":{
"message": "Integrations"
},
"integrationsDesc": {
"message": "Automatically sync secrets from Bitwarden Secrets Manager to a third-party service."
},
"sdks": {
"message": "SDKs"
},
"sdksDesc": {
"message": "Use Bitwarden Secrets Manager SDK in the following programming languages to build your own applications."
},
"setUpGithubActions": {
"message": "Set up Github Actions"
},
"setUpGitlabCICD": {
"message": "Set up GitLab CI/CD"
},
"setUpAnsible": {
"message": "Set up Ansible"
},
"cSharpSDKRepo": {
"message": "View C# repository"
},
"cPlusPlusSDKRepo": {
"message": "View C++ repository"
},
"jsWebAssemblySDKRepo": {
"message": "View JS WebAssembly repository"
},
"javaSDKRepo": {
"message": "View Java repository"
},
"pythonSDKRepo": {
"message": "View Python repository"
},
"phpSDKRepo": {
"message": "View php repository"
},
"rubySDKRepo": {
"message": "View Ruby repository"
},
"goSDKRepo": {
"message": "View Go repository"
},
"createNewClientToManageAsProvider": {
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
},

View File

@ -0,0 +1,29 @@
<div
class="tw-block tw-h-full tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-600 tw-relative tw-transition-all xl:tw-w-64 hover:tw-scale-105 focus-within:tw-outline-none focus-within:tw-ring focus-within:tw-ring-primary-700 focus-within:tw-ring-offset-2"
>
<div
class="tw-flex tw-h-36 tw-bg-secondary-100 tw-items-center tw-justify-center tw-py-2 tw-px-6 lg:tw-py-4 lg:tw-px-12"
>
<div class="tw-flex tw-items-center tw-justify-center tw-h-28 tw-w-28 lg:tw-w-40">
<img
#imageEle
[src]="image"
alt=""
class="tw-block tw-mx-auto tw-h-auto tw-max-w-full tw-max-h-full"
/>
</div>
</div>
<div class="tw-p-5">
<h3 class="tw-mb-4 tw-text-lg tw-font-semibold">{{ name }}</h3>
<a
class="tw-block tw-mb-0 tw-font-bold hover:tw-no-underline focus:tw-outline-none after:tw-content-[''] after:tw-block after:tw-absolute after:tw-w-full after:tw-h-full after:tw-left-0 after:tw-top-0"
[href]="linkURL"
[rel]="[externalURL ? 'noopener noreferrer' : null]"
>
{{ linkText }}
</a>
<span *ngIf="showNewBadge()" bitBadge class="tw-mt-3" variant="secondary">
{{ "new" | i18n }}
</span>
</div>
</div>

View File

@ -0,0 +1,174 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { BehaviorSubject } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens";
import { ThemeType } from "../../../../../../../libs/common/src/platform/enums";
import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service";
import { IntegrationCardComponent } from "./integration-card.component";
describe("IntegrationCardComponent", () => {
let component: IntegrationCardComponent;
let fixture: ComponentFixture<IntegrationCardComponent>;
const systemTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
const usersPreferenceTheme$ = new BehaviorSubject<ThemeType>(ThemeType.Light);
beforeEach(async () => {
// reset system theme
systemTheme$.next(ThemeType.Light);
await TestBed.configureTestingModule({
declarations: [IntegrationCardComponent],
providers: [
{
provide: ThemeStateService,
useValue: { selectedTheme$: usersPreferenceTheme$ },
},
{
provide: SYSTEM_THEME_OBSERVABLE,
useValue: systemTheme$,
},
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(IntegrationCardComponent);
component = fixture.componentInstance;
component.name = "Integration Name";
component.image = "test-image.png";
component.linkText = "Get started with integration";
component.linkURL = "https://example.com/";
fixture.detectChanges();
});
it("assigns link href", () => {
const link = fixture.nativeElement.querySelector("a");
expect(link.href).toBe("https://example.com/");
});
it("renders card body", () => {
const name = fixture.nativeElement.querySelector("h3");
const link = fixture.nativeElement.querySelector("a");
expect(name.textContent).toBe("Integration Name");
expect(link.textContent.trim()).toBe("Get started with integration");
});
it("assigns external rel attribute", () => {
component.externalURL = true;
fixture.detectChanges();
const link = fixture.nativeElement.querySelector("a");
expect(link.rel).toBe("noopener noreferrer");
});
describe("new badge", () => {
beforeEach(() => {
jest.useFakeTimers();
jest.setSystemTime(new Date("2023-09-01"));
});
afterEach(() => {
jest.useRealTimers();
});
it("shows when expiration is in the future", () => {
component.newBadgeExpiration = "2023-09-02";
expect(component.showNewBadge()).toBe(true);
});
it("does not show when expiration is not set", () => {
expect(component.showNewBadge()).toBe(false);
});
it("does not show when expiration is in the past", () => {
component.newBadgeExpiration = "2023-08-31";
expect(component.showNewBadge()).toBe(false);
});
it("does not show when expiration is today", () => {
component.newBadgeExpiration = "2023-09-01";
expect(component.showNewBadge()).toBe(false);
});
it("does not show when expiration is invalid", () => {
component.newBadgeExpiration = "not-a-date";
expect(component.showNewBadge()).toBe(false);
});
});
describe("imageDarkMode", () => {
it("ignores theme changes when darkModeImage is not set", () => {
systemTheme$.next(ThemeType.Dark);
usersPreferenceTheme$.next(ThemeType.Dark);
fixture.detectChanges();
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
});
describe("user prefers the system theme", () => {
beforeEach(() => {
component.imageDarkMode = "test-image-dark.png";
});
it("sets image src to imageDarkMode", () => {
usersPreferenceTheme$.next(ThemeType.System);
systemTheme$.next(ThemeType.Dark);
fixture.detectChanges();
expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png");
});
it("sets image src to light mode image", () => {
component.imageEle.nativeElement.src = "test-image-dark.png";
usersPreferenceTheme$.next(ThemeType.System);
systemTheme$.next(ThemeType.Light);
fixture.detectChanges();
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
});
});
describe("user prefers dark mode", () => {
beforeEach(() => {
component.imageDarkMode = "test-image-dark.png";
});
it("updates image to dark mode", () => {
systemTheme$.next(ThemeType.Light); // system theme shouldn't matter
usersPreferenceTheme$.next(ThemeType.Dark);
fixture.detectChanges();
expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png");
});
});
describe("user prefers light mode", () => {
beforeEach(() => {
component.imageDarkMode = "test-image-dark.png";
});
it("updates image to light mode", () => {
component.imageEle.nativeElement.src = "test-image-dark.png";
systemTheme$.next(ThemeType.Dark); // system theme shouldn't matter
usersPreferenceTheme$.next(ThemeType.Light);
fixture.detectChanges();
expect(component.imageEle.nativeElement.src).toContain("test-image.png");
});
});
});
});

View File

@ -0,0 +1,93 @@
import {
AfterViewInit,
Component,
ElementRef,
Inject,
Input,
OnDestroy,
ViewChild,
} from "@angular/core";
import { Observable, Subject, combineLatest, takeUntil } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
@Component({
selector: "sm-integration-card",
templateUrl: "./integration-card.component.html",
})
export class IntegrationCardComponent implements AfterViewInit, OnDestroy {
private destroyed$: Subject<void> = new Subject();
@ViewChild("imageEle") imageEle: ElementRef<HTMLImageElement>;
@Input() name: string;
@Input() image: string;
@Input() imageDarkMode?: string;
@Input() linkText: string;
@Input() linkURL: string;
/** Adds relevant `rel` attribute to external links */
@Input() externalURL?: boolean;
/**
* Date of when the new badge should be hidden.
* When omitted, the new badge is never shown.
*
* @example "2024-12-31"
*/
@Input() newBadgeExpiration?: string;
constructor(
private themeStateService: ThemeStateService,
@Inject(SYSTEM_THEME_OBSERVABLE)
private systemTheme$: Observable<ThemeType>,
) {}
ngAfterViewInit() {
combineLatest([this.themeStateService.selectedTheme$, this.systemTheme$])
.pipe(takeUntil(this.destroyed$))
.subscribe(([theme, systemTheme]) => {
// When the card doesn't have a dark mode image, exit early
if (!this.imageDarkMode) {
return;
}
if (theme === ThemeType.System) {
// When the user's preference is the system theme,
// use the system theme to determine the image
const prefersDarkMode =
systemTheme === ThemeType.Dark || systemTheme === ThemeType.SolarizedDark;
this.imageEle.nativeElement.src = prefersDarkMode ? this.imageDarkMode : this.image;
} else if (theme === ThemeType.Dark || theme === ThemeType.SolarizedDark) {
// When the user's preference is dark mode, use the dark mode image
this.imageEle.nativeElement.src = this.imageDarkMode;
} else {
// Otherwise use the light mode image
this.imageEle.nativeElement.src = this.image;
}
});
}
ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
/** Show the "new" badge when expiration is in the future */
showNewBadge() {
if (!this.newBadgeExpiration) {
return false;
}
const expirationDate = new Date(this.newBadgeExpiration);
// Do not show the new badge for invalid dates
if (isNaN(expirationDate.getTime())) {
return false;
}
return expirationDate > new Date();
}
}

View File

@ -0,0 +1,15 @@
<ul
class="tw-inline-grid tw-grid-cols-3 tw-gap-6 tw-m-0 tw-p-0 tw-w-full tw-auto-cols-auto tw-list-none lg:tw-grid-cols-4 lg:tw-gap-10 lg:tw-w-auto"
>
<li *ngFor="let integration of integrations">
<sm-integration-card
[name]="integration.name"
[linkText]="integration.linkText"
[linkURL]="integration.linkURL"
[image]="integration.image"
[imageDarkMode]="integration.imageDarkMode"
[externalURL]="integration.type === IntegrationType.SDK"
[newBadgeExpiration]="integration.newBadgeExpiration"
></sm-integration-card>
</li>
</ul>

View File

@ -0,0 +1,81 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens";
import { IntegrationType } from "../../../../../../../libs/common/src/enums";
import { ThemeType } from "../../../../../../../libs/common/src/platform/enums";
import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service";
import { IntegrationCardComponent } from "../integration-card/integration-card.component";
import { Integration } from "../models/integration";
import { IntegrationGridComponent } from "./integration-grid.component";
describe("IntegrationGridComponent", () => {
let component: IntegrationGridComponent;
let fixture: ComponentFixture<IntegrationGridComponent>;
const integrations: Integration[] = [
{
name: "Integration 1",
image: "test-image1.png",
linkText: "Get started with integration 1",
linkURL: "https://example.com/1",
type: IntegrationType.Integration,
},
{
name: "SDK 2",
image: "test-image2.png",
linkText: "View SDK 2",
linkURL: "https://example.com/2",
type: IntegrationType.SDK,
},
];
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [IntegrationGridComponent, IntegrationCardComponent],
providers: [
{
provide: ThemeStateService,
useValue: mock<ThemeStateService>(),
},
{
provide: SYSTEM_THEME_OBSERVABLE,
useValue: of(ThemeType.Light),
},
],
});
fixture = TestBed.createComponent(IntegrationGridComponent);
component = fixture.componentInstance;
component.integrations = integrations;
fixture.detectChanges();
});
it("lists all integrations", () => {
expect(component.integrations).toEqual(integrations);
const cards = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent));
expect(cards.length).toBe(integrations.length);
});
it("assigns the correct attributes to IntegrationCardComponent", () => {
expect(component.integrations).toEqual(integrations);
const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent))[1];
expect(card.componentInstance.name).toBe("SDK 2");
expect(card.componentInstance.image).toBe("test-image2.png");
expect(card.componentInstance.linkText).toBe("View SDK 2");
expect(card.componentInstance.linkURL).toBe("https://example.com/2");
});
it("assigns `externalURL` for SDKs", () => {
const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent));
expect(card[0].componentInstance.externalURL).toBe(false);
expect(card[1].componentInstance.externalURL).toBe(true);
});
});

View File

@ -0,0 +1,15 @@
import { Component, Input } from "@angular/core";
import { IntegrationType } from "@bitwarden/common/enums";
import { Integration } from "../models/integration";
@Component({
selector: "sm-integration-grid",
templateUrl: "./integration-grid.component.html",
})
export class IntegrationGridComponent {
@Input() integrations: Integration[];
protected IntegrationType = IntegrationType;
}

View File

@ -0,0 +1,17 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { IntegrationsComponent } from "./integrations.component";
const routes: Routes = [
{
path: "",
component: IntegrationsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class IntegrationsRoutingModule {}

View File

@ -0,0 +1,16 @@
<app-header>
<sm-new-menu></sm-new-menu>
</app-header>
<section class="tw-mb-9">
<p bitTypography="body1">{{ "integrationsDesc" | i18n }}</p>
<sm-integration-grid [integrations]="integrations"></sm-integration-grid>
</section>
<section class="tw-mb-9">
<h2 bitTypography="h2">
{{ "sdks" | i18n }}
</h2>
<p bitTypography="body1">{{ "sdksDesc" | i18n }}</p>
<sm-integration-grid [integrations]="sdks"></sm-integration-grid>
</section>

View File

@ -0,0 +1,77 @@
import { Component } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../libs/angular/src/services/injection-tokens";
import { I18nService } from "../../../../../../libs/common/src/platform/abstractions/i18n.service";
import { ThemeType } from "../../../../../../libs/common/src/platform/enums";
import { ThemeStateService } from "../../../../../../libs/common/src/platform/theming/theme-state.service";
import { I18nPipe } from "../../../../../../libs/components/src/shared/i18n.pipe";
import { IntegrationCardComponent } from "./integration-card/integration-card.component";
import { IntegrationGridComponent } from "./integration-grid/integration-grid.component";
import { IntegrationsComponent } from "./integrations.component";
@Component({
selector: "app-header",
template: "<div></div>",
})
class MockHeaderComponent {}
@Component({
selector: "sm-new-menu",
template: "<div></div>",
})
class MockNewMenuComponent {}
describe("IntegrationsComponent", () => {
let fixture: ComponentFixture<IntegrationsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
IntegrationsComponent,
IntegrationGridComponent,
IntegrationCardComponent,
MockHeaderComponent,
MockNewMenuComponent,
I18nPipe,
],
providers: [
{
provide: I18nService,
useValue: mock<I18nService>({ t: (key) => key }),
},
{
provide: ThemeStateService,
useValue: mock<ThemeStateService>(),
},
{
provide: SYSTEM_THEME_OBSERVABLE,
useValue: of(ThemeType.Light),
},
],
}).compileComponents();
fixture = TestBed.createComponent(IntegrationsComponent);
fixture.detectChanges();
});
it("divides Integrations & SDKS", () => {
const [integrationList, sdkList] = fixture.debugElement.queryAll(
By.directive(IntegrationGridComponent),
);
// Validate only expected names, as the data is constant
expect(
(integrationList.componentInstance as IntegrationGridComponent).integrations.map(
(i) => i.name,
),
).toEqual(["GitHub Actions", "GitLab CI/CD", "Ansible"]);
expect(
(sdkList.componentInstance as IntegrationGridComponent).integrations.map((i) => i.name),
).toEqual(["C#", "C++", "Go", "Java", "JS WebAssembly", "php", "Python", "Ruby"]);
});
});

View File

@ -0,0 +1,113 @@
import { Component } from "@angular/core";
import { IntegrationType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Integration } from "./models/integration";
@Component({
selector: "sm-integrations",
templateUrl: "./integrations.component.html",
})
export class IntegrationsComponent {
private integrationsAndSdks: Integration[] = [];
constructor(i18nService: I18nService) {
this.integrationsAndSdks = [
{
name: "GitHub Actions",
linkText: i18nService.t("setUpGithubActions"),
linkURL: "https://bitwarden.com/help/github-actions-integration/",
image: "../../../../../../../images/secrets-manager/integrations/github.svg",
imageDarkMode: "../../../../../../../images/secrets-manager/integrations/github-white.svg",
type: IntegrationType.Integration,
},
{
name: "GitLab CI/CD",
linkText: i18nService.t("setUpGitlabCICD"),
linkURL: "https://bitwarden.com/help/gitlab-integration/",
image: "../../../../../../../images/secrets-manager/integrations/gitlab.svg",
imageDarkMode: "../../../../../../../images/secrets-manager/integrations/gitlab-white.svg",
type: IntegrationType.Integration,
},
{
name: "Ansible",
linkText: i18nService.t("setUpAnsible"),
linkURL: "https://bitwarden.com/help/ansible-integration/",
image: "../../../../../../../images/secrets-manager/integrations/ansible.svg",
type: IntegrationType.Integration,
},
{
name: "C#",
linkText: i18nService.t("cSharpSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/csharp",
image: "../../../../../../../images/secrets-manager/sdks/c-sharp.svg",
type: IntegrationType.SDK,
},
{
name: "C++",
linkText: i18nService.t("cPlusPlusSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/cpp",
image: "../../../../../../../images/secrets-manager/sdks/c-plus-plus.png",
type: IntegrationType.SDK,
},
{
name: "Go",
linkText: i18nService.t("goSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/go",
image: "../../../../../../../images/secrets-manager/sdks/go.svg",
type: IntegrationType.SDK,
},
{
name: "Java",
linkText: i18nService.t("javaSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/java",
image: "../../../../../../../images/secrets-manager/sdks/java.svg",
imageDarkMode: "../../../../../../../images/secrets-manager/sdks/java-white.svg",
type: IntegrationType.SDK,
},
{
name: "JS WebAssembly",
linkText: i18nService.t("jsWebAssemblySDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/js",
image: "../../../../../../../images/secrets-manager/sdks/wasm.svg",
type: IntegrationType.SDK,
},
{
name: "php",
linkText: i18nService.t("phpSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/php",
image: "../../../../../../../images/secrets-manager/sdks/php.svg",
type: IntegrationType.SDK,
},
{
name: "Python",
linkText: i18nService.t("pythonSDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/python",
image: "../../../../../../../images/secrets-manager/sdks/python.svg",
type: IntegrationType.SDK,
},
{
name: "Ruby",
linkText: i18nService.t("rubySDKRepo"),
linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/ruby",
image: "../../../../../../../images/secrets-manager/sdks/ruby.png",
type: IntegrationType.SDK,
},
];
}
/** Filter out content for the integrations sections */
get integrations(): Integration[] {
return this.integrationsAndSdks.filter(
(integration) => integration.type === IntegrationType.Integration,
);
}
/** Filter out content for the SDKs section */
get sdks(): Integration[] {
return this.integrationsAndSdks.filter(
(integration) => integration.type === IntegrationType.SDK,
);
}
}

View File

@ -0,0 +1,15 @@
import { NgModule } from "@angular/core";
import { SecretsManagerSharedModule } from "../shared/sm-shared.module";
import { IntegrationCardComponent } from "./integration-card/integration-card.component";
import { IntegrationGridComponent } from "./integration-grid/integration-grid.component";
import { IntegrationsRoutingModule } from "./integrations-routing.module";
import { IntegrationsComponent } from "./integrations.component";
@NgModule({
imports: [SecretsManagerSharedModule, IntegrationsRoutingModule],
declarations: [IntegrationsComponent, IntegrationGridComponent, IntegrationCardComponent],
providers: [],
})
export class IntegrationsModule {}

View File

@ -0,0 +1,21 @@
import { IntegrationType } from "@bitwarden/common/enums";
/** Integration or SDK */
export type Integration = {
name: string;
image: string;
/**
* Optional image shown in dark mode.
*/
imageDarkMode?: string;
linkURL: string;
linkText: string;
type: IntegrationType;
/**
* Shows the "New" badge until the defined date.
* When omitted, the badge is never shown.
*
* @example "2024-12-31"
*/
newBadgeExpiration?: string;
};

View File

@ -22,6 +22,12 @@
route="machine-accounts"
[relativeTo]="route.parent"
></bit-nav-item>
<bit-nav-item
icon="bwi-providers"
[text]="'integrations' | i18n"
route="integrations"
[relativeTo]="route.parent"
></bit-nav-item>
<bit-nav-item
icon="bwi-trash"
[text]="'trash' | i18n"

View File

@ -5,6 +5,7 @@ import { AuthGuard } from "@bitwarden/angular/auth/guards";
import { organizationEnabledGuard } from "./guards/sm-org-enabled.guard";
import { canActivateSM } from "./guards/sm.guard";
import { IntegrationsModule } from "./integrations/integrations.module";
import { LayoutComponent } from "./layout/layout.component";
import { NavigationComponent } from "./layout/navigation.component";
import { OverviewModule } from "./overview/overview.module";
@ -60,6 +61,13 @@ const routes: Routes = [
titleId: "machineAccounts",
},
},
{
path: "integrations",
loadChildren: () => IntegrationsModule,
data: {
titleId: "integrations",
},
},
{
path: "trash",
loadChildren: () => TrashModule,

View File

@ -3,6 +3,7 @@ export * from "./device-type.enum";
export * from "./event-system-user.enum";
export * from "./event-type.enum";
export * from "./http-status-code.enum";
export * from "./integration-type.enum";
export * from "./native-messaging-version.enum";
export * from "./notification-type.enum";
export * from "./product-type.enum";

View File

@ -0,0 +1,4 @@
export enum IntegrationType {
Integration = "integration",
SDK = "sdk",
}