Repository: swisspost/design-system
Pull request #4685: chore(documentation): add icon detail panel and resize
Author: login: leagrdv
Created: 2025-02-17T09:52:59Z | Merged: 2025-03-12T13:10:19Z



Diff:
diff --git a/.changeset/great-avocados-fix.md b/.changeset/great-avocados-fix.md
new file mode 100644
index 0000000000..264e7cfcd3
--- /dev/null
+++ b/.changeset/great-avocados-fix.md
@@ -0,0 +1,5 @@
+---
+'@swisspost/design-system-documentation': minor
+---
+
+Added icons detail panel on documentation.
diff --git a/packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx b/packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
index a5e69e6682..73bc4aa7b8 100644
--- a/packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
+++ b/packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
@@ -1,12 +1,14 @@
 import React from 'react';
 import report from '@swisspost/design-system-icons/public/report.min.json';
-import { ReportIcon } from '@swisspost/design-system-icons/src/models/icon.model';
+import { ReportIcon, ReportSourceIcon } from '@swisspost/design-system-icons/src/models/icon.model';
 import './search-icons.styles.scss';
 
 interface Icon {
+  set: string;
   name: string;
   keywords: string;
   searchKeywords: string[];
+  sources: ReportSourceIcon[];
 }
 
 interface IconSets {
@@ -29,6 +31,8 @@ const ICON_SETS: IconSets = report.icons.reduce(
     }
 
     sets[typeOfSet as keyof IconSets].push({
+      set: typeOfSet,
+      sources: icon.stats.sources,
       name: basename,
       keywords: icon.keys.join(', '),
       searchKeywords: [basename, ...icon.keys].map(word =>
@@ -70,6 +74,8 @@ export class Search extends React.Component {
     },
   };
 
+  activeIcon: Icon | null = null;
+
   results = {
     icons: ICON_SETS.post,
     paging: {
@@ -130,6 +136,64 @@ export class Search extends React.Component {
     }
   }
 
+  popoverEventListener() {
+    document.body.style.overflow = '';
+  }
+
+  openIconDetails(icon: Icon) {
+    const popover = document.querySelector('#icon-panel') as HTMLPostPopovercontainerElement;
+    popover?.removeEventListener('postToggle', this.popoverEventListener);
+    document.body.style.overflow = 'hidden';
+    this.activeIcon = icon;
+    this.setState(this.activeIcon);
+    popover.showPopover();
+    popover?.addEventListener('postToggle', this.popoverEventListener);
+  }
+
+  iconDetailPanel() {
+    const popover = document.querySelector('#icon-panel') as HTMLPostPopovercontainerElement;
+
+    return (
+      <post-popovercontainer id="icon-panel" class="palette-default icon-panel">
+        <div className="icon-panel-content">
+          <div>
+            <div className="resizer-container">
+              <div className="resizer">
+                {this.activeIcon && <post-icon name={this.activeIcon?.name}></post-icon>}
+              </div>
+            </div>
+            <dl>
+              <dt>Set</dt>
+              <dd className="text-capitalize">{this.activeIcon?.set}</dd>
+              <dt>Name</dt>
+              <dd>{this.activeIcon?.name}</dd>
+              <dt>Download</dt>
+              <dd>
+                <a href={`/post-icons/${this.activeIcon?.name}.svg`} download>{this.activeIcon?.name}.svg</a>
+              </dd>
+              <dt>Keywords</dt>
+              <dd>{this.activeIcon?.keywords}</dd>
+              <dt>Source files</dt>
+              <dd>
+                {this.activeIcon?.sources.map((source, i) => {
+                  return (
+                    <span key={source.id}>
+                      {source.name}
+                      {i + 1 === this.activeIcon?.sources.length ? '' : ', '}
+                    </span>
+                  );
+                })}
+              </dd>
+            </dl>
+          </div>
+          <button className="btn btn-close" onClick={() => popover.hidePopover()}>
+            <span className="visually-hidden">Close</span>
+          </button>
+        </div>
+      </post-popovercontainer>
+    );
+  }
+
   searchForm() {
     return (
       <>
@@ -207,11 +271,13 @@ export class Search extends React.Component {
         {this.results.icons.slice(pageStartIndex, pageEndIndex).map((icon, i) => {
           return (
             <li className="icon" key={`icon-${icon.name}-${i}`}>
-              <div className="gfx">
-                <post-icon name={icon.name} />
-              </div>
-              <div className="name">{icon.name}</div>
-              <div className="visually-hidden">{icon.keywords}</div>
+              <button onClick={() => this.openIconDetails(icon)}>
+                <span className="gfx">
+                  <post-icon name={icon.name} />
+                </span>
+                <span className="name">{icon.name}</span>
+                <span className="visually-hidden">{icon.keywords}</span>
+              </button>
             </li>
           );
         })}
@@ -278,6 +344,7 @@ export class Search extends React.Component {
           {this.paging()}
           {this.resultsList()}
           {this.paging()}
+          {this.iconDetailPanel()}
         </div>
       </div>
     );
diff --git a/packages/documentation/src/stories/foundations/icons/search/search-icons.styles.scss b/packages/documentation/src/stories/foundations/icons/search/search-icons.styles.scss
index 74a6826243..7379fcab29 100644
--- a/packages/documentation/src/stories/foundations/icons/search/search-icons.styles.scss
+++ b/packages/documentation/src/stories/foundations/icons/search/search-icons.styles.scss
@@ -55,10 +55,16 @@ tokens.$default-map: utilities.$post-spacing;
     }
 
     .icon {
-      padding: tokens.get('utility-gap-16') 0.5rem 0.5rem;
       border: 1px solid post.$gray-20;
       border-radius: post.$border-radius;
 
+      > button {
+        padding: tokens.get('utility-gap-16') 0.5rem 0.5rem;
+        width: 100%;
+        background-color: transparent;
+        border: none;
+      }
+
       .gfx {
         display: flex;
         justify-content: center;
@@ -67,6 +73,7 @@ tokens.$default-map: utilities.$post-spacing;
       }
 
       .name {
+        display: block;
         margin-block-start: tokens.get('utility-gap-16');
         font-size: 0.8rem;
         font-weight: bold;
@@ -85,3 +92,40 @@ tokens.$default-map: utilities.$post-spacing;
     }
   }
 }
+
+.icon-panel {
+  margin: 0 0 0 auto;
+
+  > div {
+    height: 100vh;
+    max-width: 350px;
+    border: 0;
+    padding: 2rem 1rem 1rem 1rem;
+    border-left: 1px solid #e1e0dc;
+    overflow-y: auto;
+  }
+
+  .icon-panel-content {
+    display: flex;
+    align-items: flex-start;
+  }
+
+  .resizer-container {
+    width: 100px;
+    height: 100px;
+    margin: 0 auto 1rem;
+  }
+
+  dl {
+    font-size: 14px;
+
+    dt,
+    dd {
+      margin-bottom: 0.5rem;
+    }
+
+    dt {
+      font-weight: 700;
+    }
+  }
+}


Reviews:
user: login: oliverschuerch
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:09:18Z
user: login: leagrdv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T12:55:37Z
user: login: oliverschuerch
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T08:35:57Z
user: login: oliverschuerch
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T12:31:37Z

Review comments:
diff_hunk: @@ -2,8 +2,10 @@ import React from 'react';
 import report from '@swisspost/design-system-icons/public/report.json';
 import { IIcon } from '@swisspost/design-system-icons/src/models/icon.model';
 import './search-icons.styles.scss';
+import { PostPopovercontainer } from '@swisspost/design-system-components-react';
path: packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
user: login: oliverschuerch
body: Can't we just use our web-component (which should be implemented already)?
created_at: 2025-03-07T08:02:54Z
author_association: CONTRIBUTOR
path: packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
user: login: oliverschuerch
body: With the new `report.min.json` you will get some more information about our composed UI icons, so we would (for example) be able, to show every used source icon in an output UI icon.

It may be possible, that you'll face some errors, after the update, because the report.min.json is using a different structure, than the before used report.json.
Let me know, if I may give you a small introduction to its structure ;)
created_at: 2025-03-07T08:09:09Z
author_association: CONTRIBUTOR
path: packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
user: login: leagrdv
body: I've updated it, and added the list of source files!
created_at: 2025-03-10T12:55:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -130,6 +136,64 @@ export class Search extends React.Component {
     }
   }
 
+  popoverEventListener() {
+    document.body.style.overflow = '';
+  }
+
+  openIconDetails(icon: Icon) {
+    const popover = document.querySelector('#icon-panel') as HTMLPostPopovercontainerElement;
+    popover?.removeEventListener('postToggle', this.popoverEventListener);
+    document.body.style.overflow = 'hidden';
+    this.activeIcon = icon;
+    this.setState(this.activeIcon);
+    popover.showPopover();
+    popover?.addEventListener('postToggle', this.popoverEventListener);
+  }
+
+  iconDetailPanel() {
+    const popover = document.querySelector('#icon-panel') as HTMLPostPopovercontainerElement;
+
+    return (
+      <post-popovercontainer id="icon-panel" class="palette-default icon-panel">
+        <div className="icon-panel-content">
+          <div>
+            <div className="resizer-container">
+              <div className="resizer">
+                {this.activeIcon && <post-icon name={this.activeIcon?.name}></post-icon>}
+              </div>
+            </div>
+            <dl>
+              <dt>Set</dt>
+              <dd className="text-capitalize">{this.activeIcon?.set}</dd>
+              <dt>Name</dt>
+              <dd>{this.activeIcon?.name}</dd>
+              <dt>Download</dt>
+              <dd>
+                <a href={`/post-icons/${this.activeIcon?.name}.svg`}>{this.activeIcon?.name}.svg</a>
path: packages/documentation/src/stories/foundations/icons/search/search-icons.blocks.tsx
user: login: oliverschuerch
body: ```suggestion
                <a href={`/post-icons/${this.activeIcon?.name}.svg`} download>{this.activeIcon?.name}.svg</a>
```
created_at: 2025-03-12T08:34:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@swisspost/design-system-documentation': patch
path: .changeset/great-avocados-fix.md
user: login: oliverschuerch
body: ```suggestion
'@swisspost/design-system-documentation': minor
```
created_at: 2025-03-12T08:35:51Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-02-17T09:53:04Z
body: ###  🦋  Changeset detected

Latest commit: 05d3a564df12f059defa867af059fec6d3e4a4f2

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                                   | Type  |
| -------------------------------------- | ----- |
| @swisspost/design-system-documentation | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/swisspost/design-system/new/4354-resizable-icons-on-the-docs-page?filename=.changeset/tall-apricots-wash.md&value=---%0A%22%40swisspost%2Fdesign-system-documentation%22%3A%20patch%0A---%0A%0Achore(documentation)%3A%20add%20icon%20detail%20panel%20and%20resize%0A)


author_association: NONE
user: login: swisspost-bot
created_at: 2025-02-17T09:56:19Z
body: **Related Previews**
- https://preview-4685--swisspost-design-system-next.netlify.app

author_association: CONTRIBUTOR
user: login: oliverschuerch
created_at: 2025-02-21T06:47:24Z
body: Depends on #4611 
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-12T12:08:54Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=swisspost_common-web-frontend&pullRequest=4685) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=swisspost_common-web-frontend&pullRequest=4685&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=swisspost_common-web-frontend&pullRequest=4685&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=swisspost_common-web-frontend&pullRequest=4685)


author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-03-12T13:06:25Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=swisspost_common-web-frontend&pullRequest=4685) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=swisspost_common-web-frontend&pullRequest=4685&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=swisspost_common-web-frontend&pullRequest=4685&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=swisspost_common-web-frontend&pullRequest=4685&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=swisspost_common-web-frontend&pullRequest=4685)


author_association: NONE