@testgorilla/tgo-ui 11.3.0 → 11.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/fesm2022/testgorilla-tgo-ui-components-accordion.mjs +7 -7
  2. package/fesm2022/testgorilla-tgo-ui-components-ai-audio-circle.mjs +7 -7
  3. package/fesm2022/testgorilla-tgo-ui-components-ai-caveat.mjs +7 -7
  4. package/fesm2022/testgorilla-tgo-ui-components-ai-feedback.mjs +7 -7
  5. package/fesm2022/testgorilla-tgo-ui-components-alert-banner.mjs +7 -7
  6. package/fesm2022/testgorilla-tgo-ui-components-audio-waveform.mjs +10 -10
  7. package/fesm2022/testgorilla-tgo-ui-components-autocomplete-v2.mjs +15 -15
  8. package/fesm2022/testgorilla-tgo-ui-components-autocomplete.mjs +19 -19
  9. package/fesm2022/testgorilla-tgo-ui-components-avatar.mjs +10 -10
  10. package/fesm2022/testgorilla-tgo-ui-components-badge.mjs +9 -8
  11. package/fesm2022/testgorilla-tgo-ui-components-badge.mjs.map +1 -1
  12. package/fesm2022/testgorilla-tgo-ui-components-breadcrumb.mjs +7 -7
  13. package/fesm2022/testgorilla-tgo-ui-components-button.mjs +7 -7
  14. package/fesm2022/testgorilla-tgo-ui-components-card.mjs +7 -7
  15. package/fesm2022/testgorilla-tgo-ui-components-checkbox.mjs +10 -10
  16. package/fesm2022/testgorilla-tgo-ui-components-checklist.mjs +3 -3
  17. package/fesm2022/testgorilla-tgo-ui-components-core.mjs +42 -42
  18. package/fesm2022/testgorilla-tgo-ui-components-datepicker.mjs +19 -19
  19. package/fesm2022/testgorilla-tgo-ui-components-dialog.mjs +39 -25
  20. package/fesm2022/testgorilla-tgo-ui-components-dialog.mjs.map +1 -1
  21. package/fesm2022/testgorilla-tgo-ui-components-divider.mjs +7 -7
  22. package/fesm2022/testgorilla-tgo-ui-components-donut-chart.mjs +7 -7
  23. package/fesm2022/testgorilla-tgo-ui-components-dropdown.mjs +7 -7
  24. package/fesm2022/testgorilla-tgo-ui-components-elevation-shadow.mjs +7 -7
  25. package/fesm2022/testgorilla-tgo-ui-components-empty-state.mjs +7 -7
  26. package/fesm2022/testgorilla-tgo-ui-components-field.mjs +7 -7
  27. package/fesm2022/testgorilla-tgo-ui-components-file-upload.mjs +10 -10
  28. package/fesm2022/testgorilla-tgo-ui-components-filter-button.mjs +7 -7
  29. package/fesm2022/testgorilla-tgo-ui-components-gaussian-chart.mjs +7 -7
  30. package/fesm2022/testgorilla-tgo-ui-components-icon-label.mjs +7 -7
  31. package/fesm2022/testgorilla-tgo-ui-components-icon.mjs +10 -10
  32. package/fesm2022/testgorilla-tgo-ui-components-infinite-scroll-table.mjs +7 -7
  33. package/fesm2022/testgorilla-tgo-ui-components-inline-field.mjs +7 -7
  34. package/fesm2022/testgorilla-tgo-ui-components-logo.mjs +7 -7
  35. package/fesm2022/testgorilla-tgo-ui-components-media-card.mjs +3 -3
  36. package/fesm2022/testgorilla-tgo-ui-components-media-dialog.mjs +3 -3
  37. package/fesm2022/testgorilla-tgo-ui-components-media-dialog.mjs.map +1 -1
  38. package/fesm2022/testgorilla-tgo-ui-components-multi-input.mjs +7 -7
  39. package/fesm2022/testgorilla-tgo-ui-components-navbar.mjs +10 -10
  40. package/fesm2022/testgorilla-tgo-ui-components-overflow-menu.mjs +7 -7
  41. package/fesm2022/testgorilla-tgo-ui-components-page-header.mjs +7 -7
  42. package/fesm2022/testgorilla-tgo-ui-components-paginator.mjs +7 -7
  43. package/fesm2022/testgorilla-tgo-ui-components-password-criteria.mjs +7 -7
  44. package/fesm2022/testgorilla-tgo-ui-components-password-strength.mjs +7 -7
  45. package/fesm2022/testgorilla-tgo-ui-components-phone-input.mjs +10 -10
  46. package/fesm2022/testgorilla-tgo-ui-components-popover.mjs +12 -9
  47. package/fesm2022/testgorilla-tgo-ui-components-popover.mjs.map +1 -1
  48. package/fesm2022/testgorilla-tgo-ui-components-progress-bar.mjs +7 -7
  49. package/fesm2022/testgorilla-tgo-ui-components-prompt.mjs +7 -7
  50. package/fesm2022/testgorilla-tgo-ui-components-radial-progress.mjs +7 -7
  51. package/fesm2022/testgorilla-tgo-ui-components-radio-button.mjs +7 -7
  52. package/fesm2022/testgorilla-tgo-ui-components-rating.mjs +10 -10
  53. package/fesm2022/testgorilla-tgo-ui-components-scale-table.mjs +3 -3
  54. package/fesm2022/testgorilla-tgo-ui-components-scale.mjs +7 -7
  55. package/fesm2022/testgorilla-tgo-ui-components-segmented-bar.mjs +7 -7
  56. package/fesm2022/testgorilla-tgo-ui-components-segmented-button.mjs +7 -7
  57. package/fesm2022/testgorilla-tgo-ui-components-segmented-toggle.mjs +20 -5
  58. package/fesm2022/testgorilla-tgo-ui-components-segmented-toggle.mjs.map +1 -1
  59. package/fesm2022/testgorilla-tgo-ui-components-selectable-card.mjs +7 -7
  60. package/fesm2022/testgorilla-tgo-ui-components-side-navigation.mjs +7 -7
  61. package/fesm2022/testgorilla-tgo-ui-components-side-panel.mjs +6 -6
  62. package/fesm2022/testgorilla-tgo-ui-components-side-sheet.mjs +10 -10
  63. package/fesm2022/testgorilla-tgo-ui-components-skeleton.mjs +3 -3
  64. package/fesm2022/testgorilla-tgo-ui-components-slider.mjs +7 -7
  65. package/fesm2022/testgorilla-tgo-ui-components-snackbar.mjs +10 -10
  66. package/fesm2022/testgorilla-tgo-ui-components-spider-chart.mjs +7 -7
  67. package/fesm2022/testgorilla-tgo-ui-components-spinner.mjs +7 -7
  68. package/fesm2022/testgorilla-tgo-ui-components-stepper.mjs +17 -17
  69. package/fesm2022/testgorilla-tgo-ui-components-table.mjs +10 -10
  70. package/fesm2022/testgorilla-tgo-ui-components-tabs.mjs +10 -10
  71. package/fesm2022/testgorilla-tgo-ui-components-tag.mjs +7 -7
  72. package/fesm2022/testgorilla-tgo-ui-components-toggle.mjs +7 -7
  73. package/fesm2022/testgorilla-tgo-ui-components-tooltip.mjs +10 -10
  74. package/fesm2022/testgorilla-tgo-ui-components-universal-skills.mjs +17 -17
  75. package/fesm2022/testgorilla-tgo-ui-components-validation-error.mjs +7 -7
  76. package/fesm2022/testgorilla-tgo-ui-components-write-with-ai.mjs +10 -10
  77. package/fesm2022/testgorilla-tgo-ui.mjs +7 -7
  78. package/package.json +1 -1
  79. package/types/testgorilla-tgo-ui-components-badge.d.ts +2 -1
  80. package/types/testgorilla-tgo-ui-components-dialog.d.ts +11 -1
  81. package/types/testgorilla-tgo-ui-components-inline-field.d.ts +5 -6
  82. package/types/testgorilla-tgo-ui-components-popover.d.ts +3 -1
  83. package/types/testgorilla-tgo-ui-components-segmented-toggle.d.ts +11 -0
  84. package/mcp/catalog.json +0 -1
  85. package/mcp/server.mjs +0 -22048
package/mcp/catalog.json DELETED
@@ -1 +0,0 @@
1
- {"version":"11.3.0","generatedAt":"2026-08-13T13:13:56.876Z","components":{"accordion":{"name":"AccordionComponent","selector":"ui-accordion","category":"accordion","description":"Use the Accordion to organise large amounts of content into labelled, collapsible sections. It keeps pages scannable by hiding secondary or supporting information behind a header — candidates and hiring managers can expand only the sections they need. Especially useful in assessment setup flows, candidate result breakdowns, and settings pages where grouping reduces cognitive load.","importPath":"@testgorilla/tgo-ui/components/accordion","moduleName":"AccordionComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"The label displayed for the accordion.","isSignal":false},{"name":"open","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Determines whether the accordion is initially open.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Disables the accordion if set to true.","isSignal":false},{"name":"showPremiumIcon","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Indicates whether to show a premium icon.","isSignal":false},{"name":"premiumTooltipText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The tooltip text for the premium icon.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme where accordion is used","isSignal":false},{"name":"labelIcon","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Changed premium icon on this icon","isSignal":false},{"name":"variant","type":"AccordionVariant","defaultValue":"'standard'","required":false,"alias":null,"transform":null,"description":"Select accordion variant. Don`t work on Classic theme","isSignal":false},{"name":"allowCustomHeader","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Projects content with attribute 'customHeader'","isSignal":false},{"name":"noBackgroundColor","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Sets background color of content to none","isSignal":false},{"name":"toggleIconPosition","type":"ToggleIconPosition","defaultValue":"'right'","required":false,"alias":null,"transform":null,"description":"Defines the toggle icon position","isSignal":false}],"outputs":[{"name":"closed","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"opened","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"AccordionComponentModule","imports":["CommonModule","IconComponentModule","ButtonComponentModule","MatTooltipModule","MatExpansionModule","AccordionComponent"],"exports":["AccordionComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"AccordionVariant","value":"'standard' | 'filter' | 'table'"},{"name":"ToggleIconPosition","value":"'left' | 'right'"}],"interfaces":[],"enums":[]},"examples":[{"name":"WithCustomActions","template":"<ui-accordion ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n <div actions>\n <ui-icon applicationTheme=\"${appTheme}\" name=\"${appTheme === 'classic' ? 'Premium' : 'Premium-circle'}\">\n </ui-icon>\n </div>\n <div style=\"padding: 20px\">\n ${accordionText}\n </div>\n </ui-accordion>","props":{"label":"'Label'"}},{"name":"WithCustomHeader","template":"<ui-accordion ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n <div customHeader style=\"padding: 8px; display: flex\">\n <ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" label=\"Primary\" style=\"padding-right: 8px\"></ui-button>\n <ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" label=\"Ghost\"></ui-button>\n </div>\n <div style=\"padding: 20px\">\n ${accordionText}\n </div>\n </ui-accordion>","props":{"label":"'Label'","allowCustomHeader":"true"}},{"name":"Premium","template":"<ui-accordion ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n </ui-accordion>","props":{"label":"'Label'","showPremiumIcon":"true","premiumTooltipText":"'Buy Premium'"}},{"name":"RebrandFilter","template":"<ui-accordion ${argsToTemplate(args)} applicationTheme=\"dark\">\n <div [style]=\"'display: flex; gap: 10px; flex-direction: column'\">\n <ui-radio-button applicationTheme=\"${appTheme}\" [label]=\"'Option'\"></ui-radio-button>\n <ui-radio-button applicationTheme=\"${appTheme}\" [label]=\"'Option 1'\"></ui-radio-button>\n <ui-radio-button applicationTheme=\"${appTheme}\" [label]=\"'Option 2'\"></ui-radio-button>\n <ui-radio-button applicationTheme=\"${appTheme}\" [label]=\"'Option 3'\"></ui-radio-button>\n </div>\n </ui-accordion>","props":{"label":"'Label'","variant":"'filter'","labelIcon":"'Info'","premiumTooltipText":"'Label Icon'"}},{"name":"RebrandTable","template":"<ui-accordion ${argsToTemplate(args)} applicationTheme=\"dark\">\n <div actions style=\"display: flex; width: 300px;justify-content: space-between; align-items: center\">\n <div>Column info</div>\n <ui-icon size=\"24\" applicationTheme=\"${appTheme}\" name=\"Delete\"></ui-icon>\n </div>\n <div [style]=\"'display: flex; flex-direction: column'\">\n <div style=\"border-top: 1px solid #D3D3D3; width: 100%; height: 64px; display: flex; align-items: center;\">\n <div style=\"display: flex;gap: 8px; align-items: center; margin-left: 60px\">\n <ui-icon size=\"24\" applicationTheme=\"${appTheme}\" name=\"Document\"></ui-icon>\n <div>Iternal Label</div>\n </div>\n </div>\n <div style=\"border-top: 1px solid #D3D3D3; width: 100%; height: 64px; display: flex; align-items: center;\">\n <div style=\"display: flex;gap: 8px; align-items: center; margin-left: 60px\">\n <ui-icon size=\"24\" applicationTheme=\"${appTheme}\" name=\"Document\"></ui-icon>\n <div>Iternal Label</div>\n </div>\n </div>\n </div>\n </ui-accordion>","props":{"label":"'Label'","variant":"'table'","labelIcon":"'Info'","premiumTooltipText":"'Label Icon'"}}],"documentation":"Use the Accordion to organise large amounts of content into labelled, collapsible sections. It keeps pages scannable by hiding secondary or supporting information behind a header — candidates and hiring managers can expand only the sections they need. Especially useful in assessment setup flows, candidate result breakdowns, and settings pages where grouping reduces cognitive load.\n\n## When to use\n\n- **3 or more distinct sections** that don't all need to be visible at once\n- **Short, descriptive headers** — the label is the only visible cue when a section is collapsed\n- **Comparing sections side by side** — prefer Accordion over Tabs when users may want to open multiple at once\n\n## When NOT to use\n\n- **Always-relevant content** → if users need to see it every time, don't hide it\n- **Nested Accordions** → nesting inside other Accordions creates confusing depth and slows navigation\n- **The Premium variant** → don't use it unless the feature is genuinely behind an upgrade gate\n\n## How to use\n\nTo use the Accordion component, you need to import the `AccordionComponentModule` in your module:\n\n```typescript\nimport { AccordionComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n AccordionComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-accordion label=\"My Accordion\" applicationTheme=\"light\">\n <div>Content goes here</div>\n</ui-accordion>\n```\n","whenToUse":["**3 or more distinct sections** that don't all need to be visible at once","**Short, descriptive headers** — the label is the only visible cue when a section is collapsed","**Comparing sections side by side** — prefer Accordion over Tabs when users may want to open multiple at once"],"whenNotToUse":["**Always-relevant content** → if users need to see it every time, don't hide it","**Nested Accordions** → nesting inside other Accordions creates confusing depth and slows navigation","**The Premium variant** → don't use it unless the feature is genuinely behind an upgrade gate"],"template":"<mat-accordion\n class=\"accordion-wrapper\"\n [class.disabled]=\"disabled\"\n [ngClass]=\"variant\"\n [attr.theme]=\"applicationTheme\"\n>\n <mat-expansion-panel\n [expanded]=\"open\"\n [class.bgcolor-none]=\"noBackgroundColor\"\n (opened)=\"toggle(true)\"\n (closed)=\"toggle(false)\"\n >\n <mat-expansion-panel-header\n [tabIndex]=\"disabled ? -1 : 0\"\n [class.custom-height]=\"allowCustomHeader\"\n [class.left-toggle]=\"toggleIconPosition === 'left'\"\n [attr.aria-label]=\"label\"\n >\n <!-- mat-panel-title must stay a STATIC child: Material's header slots\n (ng-content select=\"mat-panel-title\") match *ngIf-desugared templates\n but NOT @if blocks, so the conditionals live inside the element. -->\n <mat-panel-title>\n @if (!allowCustomHeader) {\n @if (variant === 'table' && applicationTheme !== 'classic') {\n <div class=\"table-container\">\n <ui-icon [size]=\"'24'\" [ngClass]=\"{ open }\" [name]=\"'Arrow-chevron-down-filled'\"></ui-icon>\n <div class=\"accordion-label\">\n {{ label }}\n </div>\n </div>\n } @else {\n <div class=\"accordion-label\">\n {{ label }}\n <ng-content select=\"[subHeader]\"></ng-content>\n @if (showPremiumIcon || labelIcon) {\n <ui-icon\n [matTooltipClass]=\"applicationTheme\"\n [matTooltip]=\"premiumTooltipText\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"labelIcon ? labelIcon : applicationTheme === 'classic' ? 'Premium' : 'Premium-circle-in-line'\"\n ></ui-icon>\n }\n </div>\n }\n } @else {\n <ng-content select=\"[customHeader]\"></ng-content>\n }\n </mat-panel-title>\n <mat-panel-description>\n <div class=\"accordion-actions\">\n <ng-content select=\"[actions]\"></ng-content>\n </div>\n </mat-panel-description>\n </mat-expansion-panel-header>\n <ng-content></ng-content>\n </mat-expansion-panel>\n</mat-accordion>\n","queryGuide":{"roles":["group","region"],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":["mat-accordion","mat-expansion-panel","mat-expansion-panel-header","mat-panel-title","mat-panel-description"],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('group')","screen.getByRole('region')","screen.getByLabelText('label text')"]},"filePath":"components/accordion/accordion.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2314-16565","lastModified":1786626756370.235},"ai-audio-circle":{"name":"AiAudioCircleComponent","selector":"ui-ai-audio-circle","category":"ai-audio-circle","description":"A canvas-based audio visualiser that reacts to live microphone input in real time. Displays animated pulse and ring effects representing who is speaking during an AI interview session — the AI, the candidate, or neither. Used exclusively in the AI interview experience to provide visual feedback that the session is live and responsive.","importPath":"@testgorilla/tgo-ui/components/ai-audio-circle","moduleName":"AiAudioCircleModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"audioTrack","type":"MediaStreamTrack","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"candidateAudioTrack","type":"MediaStreamTrack","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"canvasSize","type":"number","defaultValue":"350","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"animated","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"state","type":"InterviewState","defaultValue":"InterviewState.AiListening","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"stateChange","eventType":"InterviewState","alias":null,"description":"Two-way binding change event for state","isSignal":true}],"module":{"name":"AiAudioCircleModule","imports":["CommonModule","AiAudioCircleComponent"],"exports":["AiAudioCircleComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A canvas-based audio visualiser that reacts to live microphone input in real time. Displays animated pulse and ring effects representing who is speaking during an AI interview session — the AI, the candidate, or neither. Used exclusively in the AI interview experience to provide visual feedback that the session is live and responsive.\n\n## When to use\n\n- **Active AI interview sessions** — use only where audio state needs to be communicated visually\n- **Bind the correct interview state** — always set 'ai-listening', 'ai-speaking', or 'candidate-speaking' so the animation accurately reflects what's happening\n- **Dual-track context** — pair with Audio Waveform for candidate audio feedback when both tracks need representing\n\n## When NOT to use\n\n- **Outside an AI interview context** → it has no meaning as a generic loading or decorative element\n- **Without a live or simulated audio track** → a static ring with no animation provides no value\n- **Below recommended dimensions** → canvas-based animations don't scale down gracefully\n\n## Features\n\n- **Voice-Reactive Animations**: Real-time visual feedback based on audio input levels\n- **Multiple States**: Supports different interview states (AI listening, AI speaking, candidate speaking)\n- **Canvas-Based Rendering**: High-performance animations using HTML5 Canvas\n- **Dual Audio Track Support**: Can process both AI and candidate audio tracks simultaneously\n- **Customizable Colors**: Pre-configured color schemes for pulse and ring animations\n\n## Usage\n\n### Basic Usage\n\n```typescript\nimport { AiAudioCircleModule } from '@testgorilla/tgo-ui/components/ai-audio-circle';\n\n@Component({\n selector: 'app-interview',\n template: `\n <ui-ai-audio-circle [audioTrack]=\"aiAudioTrack\" [candidateAudioTrack]=\"candidateAudioTrack\" [state]=\"currentState\">\n </ui-ai-audio-circle>\n `,\n imports: [AiAudioCircleModule],\n})\nexport class InterviewComponent {\n aiAudioTrack?: MediaStreamTrack;\n candidateAudioTrack?: MediaStreamTrack;\n currentState: InterviewState = 'ai-listening';\n}\n```\n\n### With Audio Tracks\n\nTo connect real audio tracks, obtain MediaStreamTrack objects from getUserMedia or other audio sources:\n\n```typescript\nasync setupAudioTracks() {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n this.candidateAudioTrack = stream.getAudioTracks()[0];\n}\n```\n\n## Properties\n\n| Property | Type | Default | Description |\n| --------------------- | ------------------ | ----------- | --------------------------------------------------------------------------------- |\n| `audioTrack` | `MediaStreamTrack` | `undefined` | AI audio track for visualization |\n| `candidateAudioTrack` | `MediaStreamTrack` | `undefined` | Candidate audio track for visualization |\n| `state` | `InterviewState` | `undefined` | Current interview state ('candidate-speaking' \\| 'ai-listening' \\| 'ai-speaking') |\n\n## Signals\n\n| Signal | Type | Description |\n| -------------- | ---------------- | -------------------------------------------------- |\n| `isSpeaking` | `boolean` | Indicates if audio is currently being detected |\n| `scaleValue` | `number` | Current scale value for animations (1.0 = default) |\n| `currentState` | `InterviewState` | Current state of the component |\n\n## Animation Details\n\n- **Pulse Canvas**: Inner animated blob that reacts to audio amplitude\n\nColor schemes are predefined for consistent branding:\n\n- Pulse: Blue (#277CFD), Light Blue (#99C1FE), Magenta (#D410AA)\n\n## Performance\n\nThe component uses requestAnimationFrame for smooth 60fps animations and efficiently manages canvas rendering contexts. Audio analysis is performed using the Web Audio API's AnalyserNode for minimal CPU overhead.\n","whenToUse":["**Active AI interview sessions** — use only where audio state needs to be communicated visually","**Bind the correct interview state** — always set 'ai-listening', 'ai-speaking', or 'candidate-speaking' so the animation accurately reflects what's happening","**Dual-track context** — pair with Audio Waveform for candidate audio feedback when both tracks need representing"],"whenNotToUse":["**Outside an AI interview context** → it has no meaning as a generic loading or decorative element","**Without a live or simulated audio track** → a static ring with no animation provides no value","**Below recommended dimensions** → canvas-based animations don't scale down gracefully"],"template":"<div class=\"ai-audio-visualizer\">\n <div class=\"canvas-container\">\n <canvas #pulseCanvas></canvas>\n </div>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/ai-audio-circle/ai-audio-circle.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=22008-112098","lastModified":1786626756371.0884},"ai-caveat":{"name":"AiCaveatComponent","selector":"ui-ai-caveat","category":"ai-caveat","description":"A compact, standardised disclaimer that appears below all AI-generated content. Signals to users — hiring managers and candidates alike — that the content was produced by AI and should be reviewed for accuracy. Its consistent presence builds trust by being honest about AI limitations without interrupting the reading flow.","importPath":"@testgorilla/tgo-ui/components/ai-caveat","moduleName":"AiCaveatComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"AiCaveatComponentModule","imports":["CommonModule","IconComponentModule","UiTranslatePipe","AiCaveatComponent"],"exports":["AiCaveatComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"WithContent","template":"<div class=\"content-container\">\n <div class=\"ai-content\">\n <h4>AI-Generated Summary</h4>\n <p>\n This candidate demonstrates strong problem-solving skills and technical expertise.\n They have 5 years of experience in software development and show excellent\n communication abilities. Recommended for the senior developer position.\n </p>\n </div>\n <ui-ai-caveat applicationTheme=\"${appTheme}\"></ui-ai-caveat>\n </div>","props":{}}],"documentation":"A compact, standardised disclaimer that appears below all AI-generated content. Signals to users — hiring managers and candidates alike — that the content was produced by AI and should be reviewed for accuracy. Its consistent presence builds trust by being honest about AI limitations without interrupting the reading flow.\n\n## When to use\n\n- **Below every AI block** → place directly below every block of AI-generated text, scores, or recommendations — no exceptions\n- **As a footer** → keep it as the last element in the AI content block so it reads as a footer to that content\n- **In context** → use the With Content variant to show the caveat alongside the AI output it relates to\n\n## When NOT to use\n\n- **Human or hybrid content** → don't use on human-authored content or hybrid content where the AI contribution isn't attributable\n- **Hiding the caveat** → don't suppress or hide it to make AI output appear more authoritative\n- **Editing the copy** → don't modify the caveat copy — it's a legal and trust signal, not a design element to customise\n\n## Purpose\n\n- **Transparency**: Clearly indicates that content is AI-generated\n- **Accuracy Disclaimer**: Reminds users to review content for accuracy\n- **Risk Management**: Reduces legal and reputational risk\n- **User Expectations**: Sets appropriate expectations about AI reliability\n","whenToUse":["**Below every AI block** → place directly below every block of AI-generated text, scores, or recommendations — no exceptions","**As a footer** → keep it as the last element in the AI content block so it reads as a footer to that content","**In context** → use the With Content variant to show the caveat alongside the AI output it relates to"],"whenNotToUse":["**Human or hybrid content** → don't use on human-authored content or hybrid content where the AI contribution isn't attributable","**Hiding the caveat** → don't suppress or hide it to make AI output appear more authoritative","**Editing the copy** → don't modify the caveat copy — it's a legal and trust signal, not a design element to customise"],"template":"<div class=\"ai-caveat-container\" role=\"status\" aria-live=\"polite\">\n <ui-icon [name]=\"'Info-in-line'\" [size]=\"'16'\" color=\"gray\" [applicationTheme]=\"applicationTheme\"></ui-icon>\n <span class=\"ai-caveat-text\">{{ disclaimerKey | uiTranslate | async }}</span>\n</div>\n","queryGuide":{"roles":["status"],"ariaAttributes":["aria-live"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('status')"]},"filePath":"components/ai-caveat/ai-caveat.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=21114-74325","lastModified":1786626756371.7778},"ai-feedback":{"name":"AiFeedbackComponent","selector":"ui-ai-feedback","category":"ai-feedback","description":"Lets hiring managers rate AI-generated suggestions (thumbs up / down) and optionally provide structured or free-form feedback. Appears below AI content blocks alongside the AI Caveat. Drives continuous improvement of TestGorilla's AI models by capturing in-context signal from real assessors.","importPath":"@testgorilla/tgo-ui/components/ai-feedback","moduleName":"AiFeedbackModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"positiveConfig","type":"AiFeedbackConfig","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"negativeConfig","type":"AiFeedbackConfig","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"skipPanelForPositive","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"variant","type":"AiFeedbackVariant","defaultValue":"'default'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"isRatingDisabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"enableAutoEmit","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"isPositiveFeedback","type":"boolean | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"selectionChange","eventType":"boolean | null","alias":null,"description":"","isSignal":true},{"name":"feedbackSubmitted","eventType":"AiFeedbackData","alias":null,"description":"","isSignal":true},{"name":"feedbackCancelled","eventType":"AiFeedbackData","alias":null,"description":"","isSignal":true},{"name":"feedbackChanged","eventType":"AiFeedbackData","alias":null,"description":"","isSignal":true},{"name":"isPositiveFeedbackChange","eventType":"boolean | null","alias":null,"description":"Two-way binding change event for isPositiveFeedback","isSignal":true}],"module":{"name":"AiFeedbackModule","imports":["CommonModule","AsyncPipe","ReactiveFormsModule","ButtonComponentModule","CardComponentModule","TagComponentModule","FieldComponentModule","AiCaveatComponentModule","IconComponentModule","UiTranslatePipe","MemoizeFuncPipe","AiFeedbackComponent"],"exports":["AiFeedbackComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"AiFeedbackVariant","value":"'default' | 'panel' | 'input-only'"}],"interfaces":[{"name":"AiFeedbackOption","properties":[{"name":"id","type":"string","optional":false},{"name":"label","type":"string","optional":false}]},{"name":"AiFeedbackConfig","properties":[{"name":"title","type":"string","optional":false},{"name":"options","type":"AiFeedbackOption[]","optional":true},{"name":"otherPlaceholder","type":"string","optional":true}]},{"name":"AiFeedbackData","properties":[{"name":"isPositiveFeedback","type":"boolean | null","optional":false},{"name":"selectedOptions","type":"string[]","optional":true},{"name":"otherText","type":"string","optional":true}]}],"enums":[{"name":"AiFeedbackPanelType","members":[{"name":"POSITIVE","value":"'positive'"},{"name":"NEGATIVE","value":"'negative'"}]}]},"examples":[],"documentation":"Lets hiring managers rate AI-generated suggestions (thumbs up / down) and optionally provide structured or free-form feedback. Appears below AI content blocks alongside the AI Caveat. Drives continuous improvement of TestGorilla's AI models by capturing in-context signal from real assessors.\n\n## When to use\n\n- **Always pair with the AI Caveat** — AI Feedback sits below the caveat, not above it\n- **Panel variant** when you want to collect tagged or categorised feedback after a rating\n- **skipPanelForPositive** when positive ratings are high-frequency and collecting extra detail isn't worth the friction\n\n## When NOT to use\n\n- **No AI-generated content above it** → the rating has no context without it\n- **The Input Only variant as the default** → it's for specialised flows where tags aren't meaningful\n- **Omitting the 'Rating saved' confirmation** → users need to know their feedback was captured\n\n## How to use\n\nTo use the AI Feedback component, you need to import the `AiFeedbackModule` in your module:\n\n```typescript\nimport { AiFeedbackModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n AiFeedbackModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-ai-feedback></ui-ai-feedback>\n```\n","whenToUse":["**Always pair with the AI Caveat** — AI Feedback sits below the caveat, not above it","**Panel variant** when you want to collect tagged or categorised feedback after a rating","**skipPanelForPositive** when positive ratings are high-frequency and collecting extra detail isn't worth the friction"],"whenNotToUse":["**No AI-generated content above it** → the rating has no context without it","**The Input Only variant as the default** → it's for specialised flows where tags aren't meaningful","**Omitting the 'Rating saved' confirmation** → users need to know their feedback was captured"],"template":"<div class=\"ai-feedback-wrapper\">\n <div class=\"ai-feedback\" [attr.aria-label]=\"translationContext + 'TITLE' | uiTranslate\" role=\"group\">\n <ui-ai-caveat [applicationTheme]=\"applicationTheme()\"></ui-ai-caveat>\n\n <div class=\"ai-feedback-buttons-section\">\n @if (isRatingDisabled()) {\n <div class=\"ai-feedback-saved\">\n <span class=\"ai-feedback-saved-text\">{{ translationContext + 'RATING_SAVED' | uiTranslate | async }}</span>\n <ui-icon\n [name]=\"isPositiveFeedback() ? 'Thumb-up-filled' : 'Thumb-down-filled'\"\n [filled]=\"true\"\n [applicationTheme]=\"applicationTheme()\"\n [color]=\"'gray'\"\n />\n </div>\n } @else {\n <span class=\"ai-feedback-label\">{{ translationContext + 'TITLE' | uiTranslate | async }}</span>\n\n <div\n class=\"ai-feedback-buttons\"\n role=\"group\"\n [attr.aria-expanded]=\"showPanel()\"\n [attr.aria-controls]=\"showPanel() ? 'feedback-panel' : null\"\n >\n <ui-button\n class=\"ai-feedback-button-positive\"\n data-testid=\"ai-feedback-button-positive\"\n [justIcon]=\"true\"\n [iconName]=\"'Thumb-up-in-line'\"\n [size]=\"'small'\"\n [tooltip]=\"('COMMON.YES' | uiTranslate | async)!\"\n [ariaLabel]=\"('COMMON.YES' | uiTranslate | async)!\"\n [iconFilled]=\"isPositiveFeedback() === true\"\n (buttonClickEvent)=\"onSelectionChange(true)\"\n variant=\"icon-button\"\n role=\"button\"\n >\n </ui-button>\n\n <ui-button\n data-testid=\"ai-feedback-button-negative\"\n [justIcon]=\"true\"\n [iconName]=\"'Thumb-down-in-line'\"\n [size]=\"'small'\"\n [tooltip]=\"('COMMON.NO' | uiTranslate | async)!\"\n [ariaLabel]=\"('COMMON.NO' | uiTranslate | async)!\"\n [iconFilled]=\"isPositiveFeedback() === false\"\n (buttonClickEvent)=\"onSelectionChange(false)\"\n variant=\"icon-button\"\n role=\"button\"\n >\n </ui-button>\n </div>\n }\n </div>\n </div>\n\n @if (showPanel()) {\n @let panelTitle =\n activeConfig()?.title ??\n (panelType() | memoizeFunc: getDefaultPanelTitleTranslation : this | uiTranslate | async);\n\n <ui-card\n id=\"feedback-panel\"\n class=\"feedback-panel\"\n [class.arrow-left]=\"arrowPosition() === 'left'\"\n [class.arrow-right]=\"arrowPosition() === 'right'\"\n [applicationTheme]=\"applicationTheme()\"\n [size]=\"isMobile() ? 'small' : 'medium'\"\n role=\"region\"\n [attr.aria-label]=\"panelTitle\"\n [@slideDown]\n >\n <div class=\"feedback-panel-arrow\"></div>\n\n <div class=\"feedback-panel-content\">\n <div class=\"feedback-panel-title-container\">\n <h4 class=\"feedback-panel-title\">\n {{ panelTitle }}\n </h4>\n\n <ui-button\n [justIcon]=\"true\"\n [iconName]=\"'Close-filled'\"\n [size]=\"'small'\"\n [ariaLabel]=\"'Close feedback panel'\"\n variant=\"icon-button\"\n (buttonClickEvent)=\"closePanel()\"\n >\n </ui-button>\n </div>\n\n @if (variant() === 'panel' && activeConfig()?.options && activeConfig()!.options!.length > 0) {\n <div class=\"feedback-options\">\n @for (option of activeConfig()?.options; track option.id) {\n <ui-tag\n [label]=\"option.label\"\n [isSelected]=\"isOptionSelected(option.id)\"\n [showIconWhenSelected]=\"true\"\n [applicationTheme]=\"applicationTheme()\"\n (press)=\"toggleOption(option.id)\"\n >\n </ui-tag>\n }\n </div>\n }\n\n @if (showOtherField()) {\n <ui-field\n class=\"feedback-field\"\n type=\"text\"\n [label]=\"\n activeConfig()?.otherPlaceholder ?? (translationContext + 'OTHER_PLACEHOLDER' | uiTranslate | async) ?? ''\n \"\n [formControl]=\"otherTextControl\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [showBottomContent]=\"false\"\n >\n </ui-field>\n }\n\n <div class=\"feedback-submit-container\">\n <ui-button\n [label]=\"(translationContext + 'SUBMIT' | uiTranslate | async) || ''\"\n [disabled]=\"isSubmitDisabled()\"\n [size]=\"'small'\"\n [fullWidth]=\"isMobile()\"\n (buttonClickEvent)=\"onSubmit()\"\n variant=\"primary\"\n >\n </ui-button>\n </div>\n </div>\n </ui-card>\n }\n</div>\n","queryGuide":{"roles":["group","button","region"],"ariaAttributes":["aria-label","aria-expanded","aria-controls"],"dataTestIds":["ai-feedback-button-positive","ai-feedback-button-negative"],"materialComponents":[],"childUIComponents":["ui-ai-caveat","ui-icon","ui-button","ui-card","ui-tag","ui-field"],"suggestedQueries":["screen.getByRole('group')","screen.getByRole('button')","screen.getByRole('region')","screen.getByLabelText('label text')","screen.getByTestId('ai-feedback-button-positive')","screen.getByTestId('ai-feedback-button-negative')"]},"filePath":"components/ai-feedback/ai-feedback.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=18700-52096","lastModified":1786626756372.468},"alert-banner":{"name":"AlertBannerComponent","selector":"ui-alert-banner","category":"bars","description":"A full-width, page-level notification bar that communicates important system messages — subscription expiry warnings, feature announcements, maintenance alerts, or billing issues. Persists across the page until dismissed or the condition it describes is resolved. Available in Success, Informational, Warning, and Error variants.","importPath":"@testgorilla/tgo-ui/components/alert-banner","moduleName":"AlertBannerComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"alertType","type":"AlertBarType","defaultValue":"'success'","required":false,"alias":null,"transform":null,"description":"Alert Banner type","isSignal":false},{"name":"alertVariant","type":"AlertVariant","defaultValue":"'banner'","required":false,"alias":null,"transform":null,"description":"Alert Banner variant","isSignal":false},{"name":"message","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Alert banner message","isSignal":false},{"name":"includeDismissButton","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Include dismmiss button","isSignal":false},{"name":"shadow","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Include shadow or not","isSignal":false},{"name":"linkText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Link text that will appended at the end of message","isSignal":false},{"name":"linkUrl","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Link url","isSignal":false},{"name":"linkTarget","type":"LinkTargetType","defaultValue":"'_blank'","required":false,"alias":null,"transform":null,"description":"Link target","isSignal":false},{"name":"actions","type":"AlertBannerAction[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Optional action buttons displayed on the right side of the banner.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"isFullWidth","type":"boolean | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Sets the full width","isSignal":false},{"name":"closeButtonTooltip","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Close button tooltip","isSignal":false},{"name":"hasIcon","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Shows icon","isSignal":false},{"name":"isLoading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Controls loading state for spinner visibility.","isSignal":false},{"name":"fixed","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Sets the alert banner position","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"secondaryAlerts","type":"string[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[{"name":"dismiss","eventType":"void","alias":null,"description":"Emitted when the user clicks the built-in dismiss button.","isSignal":false}],"module":{"name":"AlertBannerComponentModule","imports":["CommonModule","ButtonComponentModule","IconComponentModule","SpinnerComponentModule","UiTranslatePipe","AlertBannerComponent"],"exports":["AlertBannerComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"LinkTargetType","value":"'' | '_self' | '_blank' | '_parent' | '_top'"}],"interfaces":[{"name":"AlertBannerAction","properties":[{"name":"label","type":"string","optional":false},{"name":"onClick","type":"($event: Event) => void","optional":false},{"name":"disabled","type":"boolean","optional":true},{"name":"variant","type":"ButtonColor","optional":true},{"name":"size","type":"ButtonSize","optional":true}]}],"enums":[]},"examples":[{"name":"Types","template":"<ui-alert-banner applicationTheme=\"${appTheme}\" alertType=\"info\" message=\"This is a info alert type\"></ui-alert-banner>\n <ui-alert-banner applicationTheme=\"${appTheme}\" alertType=\"success\" message=\"This is a success alert type\"></ui-alert-banner>\n <ui-alert-banner applicationTheme=\"${appTheme}\" alertType=\"warning\" message=\"This is a warning alert type\"></ui-alert-banner>\n <ui-alert-banner applicationTheme=\"${appTheme}\" alertType=\"error\" message=\"This is a error alert type\"></ui-alert-banner>","props":{}},{"name":"WithProjectedContent","template":"<ui-alert-banner\n alertVariant=\"callout\"\n alertType=\"warning\"\n [shadow]=\"true\"\n [isFullWidth]=\"true\"\n [includeDismissButton]=\"false\"\n applicationTheme=\"${appTheme}\"\n >\n This job does not have an assessment set up.\n <a [routerLink]=\"['/create']\" [queryParams]=\"{ from: 'invite' }\">Create an assessment</a>\n to include in this invite.\n </ui-alert-banner>","props":{}}],"documentation":"A full-width, page-level notification bar that communicates important system messages — subscription expiry warnings, feature announcements, maintenance alerts, or billing issues. Persists across the page until dismissed or the condition it describes is resolved. Available in Success, Informational, Warning, and Error variants.\n\n## When to use\n\n- **Session- or page-wide messages** — use for notifications that affect the entire session or page, not a specific action (those use Snackbar)\n- **Semantic variant matched to urgency** — Error for blocking issues, Warning for time-sensitive notices, Informational for neutral announcements\n- **Short message with a clear CTA** — keep it under 1–2 sentences and pair it with a clear CTA link or button\n\n## When NOT to use\n\n- **Multiple banners at once** → don't stack multiple Alert Banners simultaneously; if there are multiple messages, prioritise the most critical\n- **Transient feedback after an action** → don't use Alert Banner for confirmations like form saved or email sent; use Snackbar\n- **Undismissible banners** → don't make the banner undismissible unless resolving the condition is truly blocking\n\n## How to use\n\nTo use the Alert Banner component, you need to import the `AlertBannerComponentModule` in your module:\n\n```typescript\nimport { AlertBannerComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n AlertBannerComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-alert-banner alertType=\"info\" message=\"This is an alert message\" applicationTheme=\"light\"> </ui-alert-banner>\n```\n\n### Projected content (inline Angular markup)\n\nThe `message` input is rendered via `[innerHTML]`, so it cannot host Angular directives such as\n`[routerLink]`. When you need a real Angular element inline in the banner body — for example a\nrouter-driven link — project the content directly instead of passing `message`:\n\n```html\n<ui-alert-banner alertVariant=\"callout\" alertType=\"warning\" [shadow]=\"true\" [isFullWidth]=\"true\">\n This job does not have an assessment set up.\n <a [routerLink]=\"createPath\" [queryParams]=\"createParams\">Create an assessment</a>\n to include in this invite.\n</ui-alert-banner>\n```\n\nNotes:\n\n- Projected content renders **in place of** `message` — leave `message` empty (or unset) when projecting.\n- Everything else (`linkText`/`linkUrl`, `actions`, `secondaryAlerts`, the dismiss button, theming and\n the `role=\"alert\"` / `aria-live` wiring) keeps working exactly as before; a consumer that passes only\n `message` renders identically.\n- A projected `<a>` is a real anchor, so it is keyboard-focusable and announced inside the alert region.\n","whenToUse":["**Session- or page-wide messages** — use for notifications that affect the entire session or page, not a specific action (those use Snackbar)","**Semantic variant matched to urgency** — Error for blocking issues, Warning for time-sensitive notices, Informational for neutral announcements","**Short message with a clear CTA** — keep it under 1–2 sentences and pair it with a clear CTA link or button"],"whenNotToUse":["**Multiple banners at once** → don't stack multiple Alert Banners simultaneously; if there are multiple messages, prioritise the most critical","**Transient feedback after an action** → don't use Alert Banner for confirmations like form saved or email sent; use Snackbar","**Undismissible banners** → don't make the banner undismissible unless resolving the condition is truly blocking"],"template":"@if (visible) {\n <div\n [ngClass]=\"cssClass\"\n [@openClose]\n role=\"alert\"\n class=\"alert-container\"\n [attr.theme]=\"applicationTheme\"\n [class.shadow]=\"alertVariant === 'callout' && shadow\"\n [attr.aria-live]=\"alertType === 'error' || alertType === 'warning' ? 'assertive' : 'polite'\"\n [attr.aria-describedby]=\"'describedby'\"\n [attr.aria-labelledby]=\"'labelledby'\"\n [tabIndex]=\"0\"\n >\n <span style=\"position: absolute; left: -9999px\" id=\"labelledby\">{{\n 'ALERT_BANNER.' + (alertType | uppercase) | uiTranslate | async\n }}</span>\n <span [style.display]=\"'none'\" id=\"describedby\">{{ ariaDescribedby }}</span>\n <div class=\"primary-alert-container\">\n <div class=\"alert-text\">\n @if (hasIcon) {\n <ui-icon\n class=\"left-icon\"\n [name]=\"iconName\"\n size=\"24\"\n [color]=\"iconColor\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-icon>\n }\n @if (isLoading) {\n <ui-spinner\n class=\"loading-spinner\"\n size=\"small\"\n [isLoader]=\"false\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-spinner>\n }\n <span [innerHTML]=\"message\"></span>\n <span class=\"projected-content\"><ng-content></ng-content></span>\n @if (!!linkText) {\n <a [target]=\"linkTarget\" [href]=\"linkUrl\">{{ linkText }}</a>\n }\n </div>\n @if (actions.length > 0) {\n <div class=\"actions-container\">\n @for (action of actions; track $index) {\n <ui-button\n class=\"action-button\"\n [variant]=\"action.variant ?? 'text-inline'\"\n [label]=\"action.label\"\n [size]=\"action.size ?? 'medium'\"\n [disabled]=\"!!action.disabled\"\n [applicationTheme]=\"applicationTheme\"\n (buttonClickEvent)=\"action.onClick($event)\"\n [fullWidth]=\"true\"\n ></ui-button>\n }\n </div>\n }\n @if (includeDismissButton && applicationTheme === 'classic') {\n <ui-button\n variant=\"text\"\n [justIcon]=\"true\"\n class=\"close\"\n iconName=\"Close\"\n (buttonClickEvent)=\"dismissClick()\"\n ></ui-button>\n }\n @if (includeDismissButton && applicationTheme !== 'classic') {\n <ui-button\n class=\"right-icon\"\n [variant]=\"'icon-button'\"\n [applicationTheme]=\"applicationTheme\"\n [iconName]=\"'Close'\"\n [size]=\"'small'\"\n [justIcon]=\"true\"\n [tooltip]=\"(closeButtonTooltip || ('COMMON.CLOSE' | uiTranslate | async))!\"\n (click)=\"dismissClick()\"\n ></ui-button>\n }\n </div>\n @if (secondaryAlerts.length > 0) {\n <div class=\"secondary-alerts-container\">\n <ul>\n @for (alert of secondaryAlerts; track alert) {\n <li class=\"secondary-alert-item\" [innerHTML]=\"alert\"></li>\n }\n </ul>\n </div>\n }\n </div>\n}\n","queryGuide":{"roles":["alert"],"ariaAttributes":["aria-live","aria-describedby","aria-labelledby"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon","ui-spinner","ui-button"],"suggestedQueries":["screen.getByRole('alert')"]},"filePath":"components/alert-banner/alert-banner.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1018-10007","lastModified":1786626756373.0872},"audio-waveform":{"name":"AudioWaveformComponent","selector":"ui-audio-waveform","category":"audio-waveform","description":"A 5-bar animated waveform that visualises microphone audio levels. Uses the Canopy AI gradient (pink to blue) and a symmetric layout where centre bars respond with higher amplitude for a natural look. Used in AI interview flows to confirm the candidate's microphone is active and picking up audio.","importPath":"@testgorilla/tgo-ui/components/audio-waveform","moduleName":"AudioWaveformModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"width","type":"number","defaultValue":"40","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"height","type":"number","defaultValue":"22","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"gap","type":"number","defaultValue":"3","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"minScale","type":"number","defaultValue":"0.08","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"smoothing","type":"number","defaultValue":"DEFAULT_AUDIO_WAVEFORM_CONFIG.smoothing","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"bars","type":"number[] | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"When provided, the component operates in controlled mode —\nit renders externally-supplied bar values instead of managing the mic itself.","isSignal":true},{"name":"active","type":"boolean | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Mirror of `bars` for controlled mode; required when `bars` is set.","isSignal":true}],"outputs":[],"module":{"name":"AudioWaveformModule","imports":["AudioWaveformComponent"],"exports":["AudioWaveformComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"AudioWaveformConfig","properties":[{"name":"barCount","type":"number","optional":false},{"name":"smoothing","type":"number","optional":false},{"name":"fftSize","type":"number","optional":false}]}],"enums":[]},"examples":[],"documentation":"A 5-bar animated waveform that visualises microphone audio levels. Uses the Canopy AI gradient (pink to blue) and a symmetric layout where centre bars respond with higher amplitude for a natural look. Used in AI interview flows to confirm the candidate's microphone is active and picking up audio.\n\n## When to use\n\n- **Controlled mode** when the parent component already manages audio state and bar values\n- **Standalone mode** when the component can own its own microphone lifecycle for simpler integration\n- **Alongside AI Audio Circle** for complete dual-track visualisation — waveform for candidate audio, circle for AI audio\n\n## When NOT to use\n\n- **Outside an AI interview or audio-recording context** → the gradient colours mark it as an AI-specific element\n- **Without an active or simulated audio source** → static bars with no movement mislead users into thinking the mic is working\n- **Changing the bar count or gradient** → both are intentional brand signals, so leave them as they are\n\n## Features\n\n- **Standalone mode**: The component manages its own `AudioWaveformService` lifecycle.\n- **Controlled mode**: Pass `bars` and `active` inputs to render externally-managed audio data.\n- **Symmetric visualization**: Middle bars show higher amplitude for a natural waveform look.\n- Fixed 5-bar layout; configurable dimensions and smoothing.\n\n## Usage\n\n### Standalone (self-managed mic)\n\n```html\n<ui-audio-waveform [width]=\"40\" [height]=\"22\" [gap]=\"3\" />\n```\n\n### Controlled (external data)\n\n```html\n<ui-audio-waveform [bars]=\"externalBars()\" [active]=\"isRecording()\" />\n```\n","whenToUse":["**Controlled mode** when the parent component already manages audio state and bar values","**Standalone mode** when the component can own its own microphone lifecycle for simpler integration","**Alongside AI Audio Circle** for complete dual-track visualisation — waveform for candidate audio, circle for AI audio"],"whenNotToUse":["**Outside an AI interview or audio-recording context** → the gradient colours mark it as an AI-specific element","**Without an active or simulated audio source** → static bars with no movement mislead users into thinking the mic is working","**Changing the bar count or gradient** → both are intentional brand signals, so leave them as they are"],"template":"<div\n class=\"waveform\"\n [style.width.px]=\"width()\"\n [style.height.px]=\"height()\"\n [style.gap.px]=\"gap()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveActive() ? 'Audio waveform active' : 'Audio waveform inactive'\"\n>\n @for (item of effectiveBars(); track $index) {\n <div\n class=\"waveform-bar\"\n [style.width.px]=\"barWidth()\"\n [style.height.px]=\"barHeight(barValue($index))\"\n [style.border-radius.px]=\"barRadius()\"\n [style.background-color]=\"barColor($index)\"\n ></div>\n }\n</div>\n","queryGuide":{"roles":["img"],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":["screen.getByRole('img')","screen.getByLabelText('label text')"]},"filePath":"components/audio-waveform/audio-waveform.component.ts","supportsTheme":false,"lastModified":1786626756373.5984},"autocomplete":{"name":"AutocompleteComponent","selector":"ui-autocomplete","category":"autocomplete","description":"A text input that surfaces a filtered dropdown of options as the user types. Supports single and multi-select modes, group headings, and an 'allow add' mode for creating new values inline. Used in assessment setup for tagging, skill selection, and filtering candidates by criteria.","importPath":"@testgorilla/tgo-ui/components/autocomplete","moduleName":"AutocompleteComponentModule","standalone":false,"changeDetection":"OnPush","inputs":[{"name":"itemsList","type":"any[] | { groupName: string; items: any[]; isGroup: boolean }[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"suggestionsList","type":"any[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"allowAdd","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"textField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"valueField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"type","type":"AutocompleteType","defaultValue":"'multi'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"minCharactersSearch","type":"number","defaultValue":"3","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"variant","type":"DropdownVariationType","defaultValue":"DropdownVariation.AUTOCOMPLETE","required":true,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"showBottomContent","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show reserved content below form field","isSignal":false},{"name":"valuePrimitive","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Return full object if true","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator of the autocomplete width","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"required","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Autocomplete is required or not","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"'#FFF2FC'","required":false,"alias":null,"transform":null,"description":"Color of the option hover.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in-build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"virtualScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Sets dropdown list to virtual scroll mode","isSignal":false},{"name":"compareWith","type":"(a: any, b: any) => boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"dropdownPanelClass","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"closeOnScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"size","type":"AutocompleteSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"allowUserInput","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"clearable","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"exactMatchSelect","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"maxItemSelected","type":"unknown","defaultValue":"Infinity","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"maxLength","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"isDynamicFilteringEnabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"dynamicFilterLoading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"dynamicFilteredList","type":"any[] | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"infiniteScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"hasMoreOptionsForInfiniteScroll","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"itemValue","type":"any","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input field errors","isSignal":false}],"outputs":[{"name":"selectionChange","eventType":"any","alias":null,"description":"","isSignal":false},{"name":"searchTextChange","eventType":"string","alias":null,"description":"","isSignal":false},{"name":"blur","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"focus","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"scrollViewportBottomReached","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"scrollViewportBottomReachedFiltered","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"AutocompleteComponentModule","imports":["CommonModule","MatFormFieldModule","MatAutocompleteModule","MatInputModule","FormsModule","TagComponentModule","MatChipsModule","IconComponentModule","UiTranslatePipe","ValidationErrorModule","HasValidationErrorPipe","CdkVirtualForOf","CdkVirtualScrollViewport","CdkFixedSizeVirtualScroll","PreventInputDirective","SpinnerComponentModule"],"exports":["AutocompleteComponent"],"declarations":["AutocompleteComponent","TransformItemPipe","IncludesPipe","SelectTextDirective"],"providers":["TransformItemPipe","IncludesPipe","SelectTextDirective"]},"types":{"typeAliases":[{"name":"AutocompleteType","value":"'single' | 'multi' | 'large'"},{"name":"DropdownVariationType","value":"`${DropdownVariation}`"},{"name":"AutocompleteSize","value":"'small' | 'medium'"}],"interfaces":[],"enums":[{"name":"Autocomplete","members":[{"name":"SINGLE","value":"'single'"},{"name":"MULTI","value":"'multi'"},{"name":"LARGE","value":"'large'"}]},{"name":"Padding","members":[{"name":"AUTOCOMPLETE_CONTAINER","value":"40"},{"name":"DROPDOWN_CONTAINER","value":"60"},{"name":"AUTOCOMPLETE_SPACE_CONTAINER","value":"65"},{"name":"DROPDOWN_SPACE_CONTAINER","value":"85"}]},{"name":"Timeout","members":[{"name":"VIEW_INITIALIZED_AFTER_ACTION","value":"150"},{"name":"VIEW_INITIALIZED_COMPONENT_LOAD","value":"600"}]},{"name":"DropdownVariation","members":[{"name":"AUTOCOMPLETE","value":"'autocomplete'"},{"name":"DROPDOWN","value":"'dropdown'"}]}]},"examples":[],"documentation":"A text input that surfaces a filtered dropdown of options as the user types. Supports single and multi-select modes, group headings, and an 'allow add' mode for creating new values inline. Used in assessment setup for tagging, skill selection, and filtering candidates by criteria.\n\n> **Prefer Autocomplete v2.** This component is superseded by Autocomplete v2 (`AutocompleteV2Component`), the recommended implementation — use it for new work wherever possible.\n\n## When to use\n\n- **Multi-select mode** when users need to apply several values at once (e.g. selecting multiple job roles or skills)\n- **Dropdown variant** when the full option list should be shown on focus, not only on type\n- **Both textField and valueField defined** so the component binds correctly to your data model\n\n## When NOT to use\n\n- **Fewer than ~6 options** → a standard Dropdown is less cognitive overhead\n- **Allow Add without model support** → don't enable it unless the data model supports user-created values; orphaned custom entries break downstream filters\n- **No error state** → if validation fails on a multi-select field, the error must be visible\n\n## How to use\n\nTo use the Autocomplete component, you need to import the `AutocompleteComponentModule` in your module:\n\n```typescript\nimport { AutocompleteComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n AutocompleteComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-autocomplete\n [itemsList]=\"items\"\n [suggestionsList]=\"suggestions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [variant]=\"'autocomplete'\"\n [type]=\"'multi'\"\n applicationTheme=\"light\"\n>\n</ui-autocomplete>\n```\n","whenToUse":["**Multi-select mode** when users need to apply several values at once (e.g. selecting multiple job roles or skills)","**Dropdown variant** when the full option list should be shown on focus, not only on type","**Both textField and valueField defined** so the component binds correctly to your data model"],"whenNotToUse":["**Fewer than ~6 options** → a standard Dropdown is less cognitive overhead","**Allow Add without model support** → don't enable it unless the data model supports user-created values; orphaned custom entries break downstream filters","**No error state** → if validation fails on a multi-select field, the error must be visible"],"template":"<ng-container>\n <div class=\"autocomplete-wrapper\" [ngClass]=\"{ 'full-width': fullWidth, disabled: disabled }\">\n <mat-form-field\n [color]=\"\n safeErrors.length ||\n (ngControl?.errors && ngControl?.touched) ||\n (AutocompleteUtils.isArray(value) && value.length) > maxItemSelected ||\n ((AutocompleteUtils.isArray(value) && value.length) >= maxItemSelected && inputValue && inputValue !== ' ')\n ? 'warn'\n : 'accent'\n \"\n [appearance]=\"'outline'\"\n [ngClass]=\"{\n 'large-size': type === autocompleteType.LARGE,\n 'hide-bottom-content': !showBottomContent,\n 'keyboard-focused': keyboardFocused(),\n 'has-label': !!label,\n }\"\n >\n @if (label) {\n <mat-label\n >{{ label }}\n @if (required) {\n <span>*</span>\n }\n </mat-label>\n }\n <div #formField class=\"input-container\">\n @if (textField ? value?.[textField] : value === inputValue) {\n <span style=\"position: absolute; left: -9999px\" aria-live=\"polite\">{{\n ('SELECTED' | uiTranslate | async) + ' ' + inputValue\n }}</span>\n }\n\n <input\n data-testid=\"autocomplete-input\"\n #trigger=\"matAutocompleteTrigger\"\n #matInput\n [attr.maxlength]=\"maxLength !== null && maxLength !== undefined ? maxLength : null\"\n [uiPreventInput]=\"!allowUserInput\"\n [disabled]=\"disabled\"\n [ngClass]=\"{ 'unset-margin': value && type === autocompleteType.LARGE, 'no-caret': !allowUserInput }\"\n (blur)=\"refillInput()\"\n (focusin)=\"onFocus()\"\n [attr.aria-label]=\"\n (translationContext + 'SELECTED' | uiTranslate | async) + ' ' + (value | transformItem: textField : true)\n \"\n [style.opacity]=\"type === autocompleteType.MULTI ? inputOpacity : 1\"\n matInput\n [placeholder]=\"value && value.length ? '' : placeholder\"\n [type]=\"'text'\"\n [matAutocomplete]=\"autocomplete\"\n (input)=\"onInputChange($event)\"\n [(ngModel)]=\"inputValue\"\n />\n @if (\n (type === autocompleteType.LARGE && value) ||\n (!(inputValue && isInputFocus) && value && type !== autocompleteType.SINGLE)\n ) {\n <div class=\"selected-items\" #tagContainer>\n @for (valueItem of value; track valueItem; let i = $index) {\n <ui-tag\n class=\"value-tag\"\n [id]=\"'ui-tag-' + i\"\n [label]=\"valueItem | transformItem: textField\"\n [allowClose]=\"true\"\n [isDisabled]=\"disabled\"\n [applicationTheme]=\"applicationTheme\"\n [style.opacity]=\"type === autocompleteType.MULTI ? inputOpacity : 1\"\n [style.display]=\"\n (isOverLapping && i > 0 && i >= overlapIndex) || (size === 'small' && value.length > 1)\n ? 'none'\n : 'block'\n \"\n [ngStyle]=\"{\n '--tag-max-width': type === autocompleteType.LARGE ? 'fit-content' : firstTagMaxWidth + 'px',\n }\"\n [ngClass]=\"{\n 'overlap-tag':\n isOverlapChecking && overlapIndex && i >= overlapIndex && type === autocompleteType.MULTI,\n }\"\n (close)=\"onOptionRemoved(valueItem)\"\n ></ui-tag>\n }\n @if (size === 'small') {\n @if (value.length > 1) {\n <div class=\"selected-count\">\n {{\n translationContext + 'SELECTED_AMOUNT'\n | uiTranslate: { numberSelected: (value | slice: overlapIndex).length }\n | async\n }}\n </div>\n }\n } @else {\n @if (value.length > 1 && isOverLapping) {\n <div class=\"overlap-count\">\n <ui-tag\n [applicationTheme]=\"applicationTheme\"\n [isDisabled]=\"disabled\"\n [label]=\"'+' + (value.length - overlapIndex)\"\n ></ui-tag>\n </div>\n }\n }\n </div>\n }\n </div>\n\n @if (\n (clearable && type === autocompleteType.SINGLE && (value?.length || hasObjectValue)) ||\n (type !== autocompleteType.SINGLE &&\n value &&\n value.length > 1 &&\n ((AutocompleteUtils.isPrimitive(value) && value.length) ||\n (!AutocompleteUtils.isPrimitive(value) &&\n ((AutocompleteUtils.isArray(value) && value.length) ||\n (!AutocompleteUtils.isArray(value) && !value.length)))))\n ) {\n <ui-icon\n class=\"remove-selected\"\n [tabIndex]=\"disabled ? -1 : 0\"\n (keydown.enter)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n (keydown.space)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n [ngClass]=\"{ 'large-input-icon': type === autocompleteType.LARGE }\"\n (click)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n [size]=\"'24'\"\n [color]=\"applicationTheme === 'classic' ? 'inherit' : 'rebrand-black'\"\n [attr.aria-label]=\"'COMMON.CLEAR' | uiTranslate | async\"\n [name]=\"applicationTheme === 'classic' ? 'Close' : 'Close-in-line'\"\n ></ui-icon>\n }\n\n @if (isDropdown) {\n <ui-icon\n class=\"arrow-state\"\n [ngClass]=\"{ opened: autocomplete.isOpen, 'large-input-icon': type === autocompleteType.LARGE }\"\n [size]=\"'24'\"\n [name]=\"applicationTheme !== 'classic' ? 'Arrow-chevron-down-in-line' : 'Arrow_down'\"\n (click)=\"onChevronClick(autocomplete)\"\n ></ui-icon>\n }\n\n <mat-autocomplete\n #autocomplete\n [class]=\"applicationTheme + ' ' + dropdownPanelClass\"\n (optionSelected)=\"onOptionSelected($event)\"\n [disableRipple]=\"applicationTheme !== 'classic'\"\n [hideSingleSelectionIndicator]=\"false\"\n (opened)=\"gotoUp()\"\n >\n @if (type !== autocompleteType.MULTI || value?.length < maxItemSelected) {\n @if ((searchResult$ | async) === false) {\n @if (!isDropdown) {\n @if (suggestionsList.length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n @for (suggested of suggestionsList; track suggested) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': suggested | includes: value : valueField }\"\n [value]=\"suggested\"\n >\n <span [selectText]=\"userInput$ | async\">{{ suggested | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n }\n @if (value?.length && type !== autocompleteType.SINGLE) {\n <mat-optgroup [label]=\"(translationContext + 'SELECTED' | uiTranslate | async)! | uppercase\">\n @for (item of value; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span>{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n }\n @if (isGrouped) {\n @for (group of itemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.items; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.items; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n }\n @if (value?.length && type === autocompleteType.SINGLE) {\n <mat-optgroup [label]=\"(translationContext + 'SELECTED' | uiTranslate | async)! | uppercase\">\n <mat-option (mousedown)=\"$event.preventDefault()\" [class]=\"'selected-option'\" [value]=\"value\">\n <span [selectText]=\"userInput$ | async\">{{ value | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n </mat-optgroup>\n }\n } @else {\n @if (virtualScroll || infiniteScroll) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"48\"\n [style.height.px]=\"getVirtualScrollHeight(itemsList.length)\"\n class=\"virtual-scroll\"\n >\n @if (!isGrouped) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of itemsList\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n @if (infiniteScroll && hasMoreOptionsForInfiniteScroll) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n }\n } @else {\n @for (group of itemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of group.items\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n </mat-optgroup>\n } @else {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of group.items\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n @if (!isGrouped) {\n @for (item of itemsList; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n } @else {\n @for (group of itemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.items; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.items; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n }\n }\n }\n }\n\n @if (filteredSuggestionList$ | async; as filteredSuggestionList) {\n @if (filteredItemsList$ | async; as filteredItemsList) {\n @if (virtualScroll || infiniteScroll) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"48\"\n [style.height.px]=\"getVirtualScrollHeight(filteredItemsList.length)\"\n class=\"virtual-scroll\"\n >\n @if (isGrouped && filteredItemsList.length) {\n @for (group of filteredItemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of group.filteredItems\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n </mat-optgroup>\n } @else {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of group.filteredItems\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n @if (filteredSuggestionList.length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n *cdkVirtualFor=\"let item of filteredSuggestionList\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n </mat-optgroup>\n }\n @if (!filteredSuggestionList.length && !isGrouped) {\n @for (item of filteredItemsList; let idx = $index; track item[valueField] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n } @else {\n @if (filteredItemsList.length && !isGrouped) {\n <mat-optgroup [label]=\"(translationContext + 'ALL_ITEMS' | uiTranslate | async)! | uppercase\">\n @for (item of filteredItemsList; let idx = $index; track item[valueField] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n }\n }\n @if (isDynamicFilteringEnabled && dynamicFilterLoading) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n } @else if (inputValue) {\n @if (allowAdd && !isAlreadyAdded) {\n <mat-option (mousedown)=\"$event.preventDefault()\" [value]=\"inputValue\">\n <span>{{ 'COMMON.ADD' | uiTranslate | async }}</span>\n <span class=\"add-suggestion\">\"{{ inputValue }}\"</span>\n </mat-option>\n } @else if (!filteredItemsList.length && !filteredSuggestionList.length) {\n <mat-option [style.pointer-events]=\"'none'\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </mat-option>\n }\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n @if (isGrouped && filteredItemsList.length) {\n @for (group of filteredItemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.filteredItems; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.filteredItems; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n }\n @if (isGrouped && filteredItemsList.length) {\n @for (group of filteredItemsList; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.filteredItems; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.filteredItems; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n }\n }\n }\n @if (filteredSuggestionList.length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n @for (item of filteredSuggestionList; track item) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n }\n @if (!filteredSuggestionList.length && !isGrouped) {\n @for (item of filteredItemsList; let idx = $index; track item[valueField] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n } @else {\n @if (filteredItemsList.length && !isGrouped) {\n <mat-optgroup [label]=\"(translationContext + 'ALL_ITEMS' | uiTranslate | async)! | uppercase\">\n @for (item of filteredItemsList; let idx = $index; track item[valueField] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: value : valueField }\"\n [value]=\"item\"\n >\n <span [selectText]=\"userInput$ | async\">{{ item | transformItem: textField }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </mat-option>\n }\n </mat-optgroup>\n }\n }\n @if (isDynamicFilteringEnabled && dynamicFilterLoading) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n } @else if (inputValue) {\n @if (allowAdd && !isAlreadyAdded) {\n <mat-option (mousedown)=\"$event.preventDefault()\" [value]=\"inputValue\">\n <span>{{ 'COMMON.ADD' | uiTranslate | async }}</span>\n <span class=\"add-suggestion\">\"{{ inputValue }}\"</span>\n </mat-option>\n } @else if (!filteredItemsList.length && !filteredSuggestionList.length) {\n <mat-option [style.pointer-events]=\"'none'\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </mat-option>\n }\n }\n }\n }\n }\n }\n </mat-autocomplete>\n @if (\n safeErrors.length ||\n (ngControl?.errors | hasValidationError) ||\n (AutocompleteUtils.isArray(value) && value.length) > maxItemSelected ||\n ((AutocompleteUtils.isArray(value) && value.length) >= maxItemSelected && inputValue && inputValue !== ' ')\n ) {\n <mat-hint class=\"error\">\n @if (\n (AutocompleteUtils.isArray(value) && value.length) > maxItemSelected ||\n ((AutocompleteUtils.isArray(value) && value.length) >= maxItemSelected && inputValue && inputValue !== ' ')\n ) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{\n translationContext + 'MAX_SELECTED_ERROR' | uiTranslate: { max: maxItemSelected } | async\n }}</span>\n </div>\n }\n @if (safeErrors.length) {\n @for (error of safeErrors; track trackByFn($index, error)) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span [innerHTML]=\"error\"></span>\n </div>\n }\n }\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error\n [ngControl]=\"ngControl\"\n [label]=\"hideLabelInErrors ? null : label\"\n ></ui-validation-error>\n }\n </mat-hint>\n }\n </mat-form-field>\n </div>\n</ng-container>\n","queryGuide":{"roles":["option"],"ariaAttributes":["aria-live","aria-label"],"dataTestIds":["autocomplete-input"],"materialComponents":["mat-form-field","mat-label","mat-autocomplete","mat-optgroup","mat-option","mat-hint"],"childUIComponents":["ui-tag","ui-icon","ui-spinner","ui-validation-error"],"suggestedQueries":["screen.getByRole('option')","screen.getByLabelText('label text')","screen.getByTestId('autocomplete-input')"]},"filePath":"components/autocomplete/autocomplete.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1266-11089","lastModified":1786626756377.009},"autocomplete-v2":{"name":"AutocompleteV2Component","selector":"ui-autocomplete-v2","category":"autocomplete-v2","description":"AutocompleteV2Component component","importPath":"@testgorilla/tgo-ui/components/autocomplete-v2","moduleName":"AutocompleteV2ComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"itemsList","type":"T[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"suggestionsList","type":"T[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"groupsList","type":"AutocompleteGroup<T>[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"allowAdd","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"textField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"valueField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"imageField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"type","type":"AutocompleteType","defaultValue":"'multi'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"minCharactersSearch","type":"number","defaultValue":"3","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"variant","type":"DropdownVariationType","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"showBottomContent","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"required","type":"boolean | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"virtualScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"compareWith","type":"((a: T, b: T) =","defaultValue":"(a: T, b: T) => boolean","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"dropdownPanelClass","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"panelPosition","type":"MatAutocompleteTrigger['position']","defaultValue":"'auto'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"hideSelectedSection","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"userAdditionsGroupLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"closeOnScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"size","type":"AutocompleteSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"allowUserInput","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"clearable","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxLength","type":"number | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"dynamicFilterLoading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"dynamicFilteredList","type":"T[] | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"infiniteScroll","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"hasMoreOptionsForInfiniteScroll","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"itemValue","type":"T | T[] | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"formErrorConfig","type":"Record<string, string>","defaultValue":"{}","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"errors","type":"string[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"addOptionPosition","type":"AddOptionPosition","defaultValue":"'bottom'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"tagsPosition","type":"TagsPosition","defaultValue":"'inside'","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"selectionChange","eventType":"T | T[]","alias":null,"description":"","isSignal":true},{"name":"searchTextChange","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"blur","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"focus","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"scrollViewportBottomReached","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"scrollViewportBottomReachedFiltered","eventType":"void","alias":null,"description":"","isSignal":true}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"AutocompleteType","value":"'single' | 'multi' | 'large'"},{"name":"DropdownVariationType","value":"`${DropdownVariation}`"},{"name":"AutocompleteSize","value":"'small' | 'medium'"},{"name":"AddOptionPosition","value":"'top' | 'bottom'"},{"name":"TagsPosition","value":"'inside' | 'below'"}],"interfaces":[],"enums":[{"name":"Autocomplete","members":[{"name":"SINGLE","value":"'single'"},{"name":"MULTI","value":"'multi'"},{"name":"LARGE","value":"'large'"}]},{"name":"DropdownVariation","members":[{"name":"AUTOCOMPLETE","value":"'autocomplete'"},{"name":"DROPDOWN","value":"'dropdown'"}]}]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<ng-container>\n <div class=\"autocomplete-wrapper\" [ngClass]=\"{ disabled: _isDisabled() }\">\n <mat-form-field\n [color]=\"hasError() ? 'warn' : 'accent'\"\n [appearance]=\"'outline'\"\n [ngClass]=\"{\n 'large-size': type() === autocompleteType.LARGE,\n 'hide-bottom-content': !showBottomContent(),\n 'keyboard-focused': keyboardFocused(),\n 'has-label': !!label(),\n }\"\n >\n @if (label()) {\n <mat-label\n >{{ label() }}\n @if (required()) {\n <span>*</span>\n }\n </mat-label>\n }\n <div\n #formField\n class=\"input-container\"\n [class.has-multiple-selections]=\"\n type() === autocompleteType.LARGE &&\n utils.isArray(valueManager.value()) &&\n $any(valueManager.value()).length > 1\n \"\n [class.autocomplete-variant]=\"type() === autocompleteType.LARGE && !isDropdown\"\n >\n @if (textField() ? $any(valueManager.value())?.[textField()] : $any(valueManager.value()) === inputValue()) {\n <span style=\"position: absolute; left: -9999px\" aria-live=\"polite\">{{\n ('SELECTED' | uiTranslate | async) + ' ' + inputValue()\n }}</span>\n }\n\n <input\n data-testid=\"autocomplete-v2-input\"\n #trigger=\"matAutocompleteTrigger\"\n #matInput\n [attr.maxlength]=\"maxLength() !== null && maxLength() !== undefined ? maxLength() : null\"\n [uiPreventInput]=\"!allowUserInput()\"\n [disabled]=\"_isDisabled()\"\n [ngClass]=\"{\n 'unset-margin': valueManager.value() && type() === autocompleteType.LARGE,\n 'no-caret': !allowUserInput(),\n }\"\n (blur)=\"onBlur()\"\n (focusin)=\"onFocus()\"\n [attr.aria-label]=\"\n (translationContext + 'SELECTED' | uiTranslate | async) +\n ' ' +\n (valueManager.value() | transformItem: textField() : true)\n \"\n matInput\n [placeholder]=\"suppressPlaceholder() ? '' : placeholder()\"\n [type]=\"'text'\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"panelPosition()\"\n (input)=\"onInputChange($event)\"\n [ngModel]=\"inputValue()\"\n (ngModelChange)=\"inputValue.set($event)\"\n />\n <!-- Hidden measurement container: always in DOM, renders ALL tags for width measurement -->\n @if (!tagsBelow() && type() !== autocompleteType.SINGLE && utils.isArray(valueManager.value())) {\n <div class=\"selected-items measurement-container\" #measureEl>\n @for (valueItem of $any(valueManager.value()); track valueItem[valueField()] || i; let i = $index) {\n <ui-tag\n class=\"value-tag\"\n [id]=\"'measure-tag-' + i\"\n [label]=\"valueItem | transformItem: textField()\"\n [allowClose]=\"true\"\n [isDisabled]=\"disabled()\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-tag>\n }\n </div>\n }\n\n @if (\n !tagsBelow() &&\n ((type() === autocompleteType.LARGE && valueManager.value()) ||\n (!(inputValue() && isInputFocus()) && valueManager.value() && type() !== autocompleteType.SINGLE))\n ) {\n <!-- Visible container: only renders tags that fit. Hidden until first measurement to prevent flash. -->\n <div\n class=\"selected-items\"\n #tagContainer\n uiAutocompleteOverflowDetect\n #overflow=\"autocompleteOverflowDetect\"\n [measureContainer]=\"measureElRef?.nativeElement\"\n [class.measured]=\"\n type() === autocompleteType.LARGE || type() === autocompleteType.SINGLE || overflow.hasMeasured()\n \"\n >\n @if (size() === 'small' && $any(valueManager.value()).length > 1) {\n <div class=\"selected-count\">\n {{\n translationContext + 'SELECTED_AMOUNT'\n | uiTranslate: { numberSelected: valueManager.value()!.length }\n | async\n }}\n </div>\n } @else {\n @for (valueItem of $any(valueManager.value()); track valueItem[valueField()] || i; let i = $index) {\n @if (i < overflow.visibleCount()) {\n <ui-tag\n class=\"value-tag\"\n [id]=\"'ui-tag-' + i\"\n [label]=\"valueItem | transformItem: textField()\"\n [allowClose]=\"true\"\n [isDisabled]=\"disabled()\"\n [applicationTheme]=\"applicationTheme\"\n [ngStyle]=\"\n type() !== autocompleteType.LARGE\n ? { '--tag-max-width': overflow.tagMaxWidth() ? overflow.tagMaxWidth() + 'px' : 'fit-content' }\n : {}\n \"\n (close)=\"onOptionRemoved(valueItem)\"\n ></ui-tag>\n }\n }\n @if (overflow.overflowCount() > 0) {\n <div class=\"overlap-count\" [matTooltip]=\"hiddenItemsTooltip()\">\n <ui-tag\n [applicationTheme]=\"applicationTheme\"\n [isDisabled]=\"disabled()\"\n [label]=\"'+' + overflow.overflowCount()\"\n ></ui-tag>\n </div>\n }\n }\n </div>\n }\n </div>\n\n @if (showClearButton()) {\n <ui-icon\n class=\"remove-selected\"\n [tabIndex]=\"_isDisabled() ? -1 : 0\"\n (keydown.enter)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n (keydown.space)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n [ngClass]=\"{ 'large-input-icon': type() === autocompleteType.LARGE }\"\n (click)=\"onClear(); $event.preventDefault(); $event.stopPropagation()\"\n [size]=\"'24'\"\n [color]=\"applicationTheme === 'classic' ? 'inherit' : 'rebrand-black'\"\n [attr.aria-label]=\"'COMMON.CLEAR' | uiTranslate | async\"\n [name]=\"applicationTheme === 'classic' ? 'Close' : 'Close-in-line'\"\n ></ui-icon>\n }\n\n @if (isDropdown) {\n <ui-icon\n class=\"arrow-state\"\n [ngClass]=\"{ opened: autocomplete.isOpen, 'large-input-icon': type() === autocompleteType.LARGE }\"\n [size]=\"'24'\"\n [name]=\"applicationTheme !== 'classic' ? 'Arrow-chevron-down-in-line' : 'Arrow_down'\"\n (click)=\"onChevronClick(autocomplete)\"\n ></ui-icon>\n }\n\n <mat-autocomplete\n #autocomplete\n [class]=\"applicationTheme + ' ' + dropdownPanelClass()\"\n (optionSelected)=\"onOptionSelected($event)\"\n [disableRipple]=\"applicationTheme !== 'classic'\"\n [hideSingleSelectionIndicator]=\"false\"\n (opened)=\"onPanelOpened()\"\n >\n <ng-template #optionContentTpl let-item=\"item\" let-highlight=\"highlight\">\n @if (imageField() && item?.[imageField()]) {\n <img class=\"option-image\" [src]=\"item[imageField()]\" alt=\"\" />\n }\n <span [selectText]=\"highlight ? (filterService.userInput$ | async) : null\">{{\n item | transformItem: textField()\n }}</span>\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Check' : 'Check-in-line'\"\n ></ui-icon>\n </ng-template>\n\n <!-- Reusable option template — must be inside mat-autocomplete for @ContentChildren to find mat-options -->\n <ng-template #optionTpl let-item=\"item\" let-highlight=\"highlight\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: highlight }\"\n ></ng-container>\n </mat-option>\n </ng-template>\n\n <!-- Reusable \"Add\"/no-results/loading option for the filtered view -->\n <ng-template #addOptionTpl let-items=\"items\" let-suggestions=\"suggestions\">\n @if (dynamicFilterLoading()) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n } @else if (inputValue()) {\n @if (\n allowAdd() && !isAlreadyAdded() && !utils.hasExactMatch(items, suggestions, inputValue(), textField())\n ) {\n <mat-option (mousedown)=\"$event.preventDefault()\" [value]=\"inputValue()\">\n <span>{{ 'COMMON.ADD' | uiTranslate | async }}</span>\n <span class=\"add-suggestion\">\"{{ inputValue() }}\"</span>\n </mat-option>\n } @else if (!items.length && !suggestions.length) {\n <mat-option [style.pointer-events]=\"'none'\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </mat-option>\n }\n }\n </ng-template>\n\n <!-- ── Unfiltered ───────────────────────────────────────────────── -->\n @if ((filterService.isSearching$ | async) === false) {\n @if (!isDropdown) {\n <!-- Suggestions -->\n @if (suggestionsList().length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n @for (suggested of suggestionsList(); track suggested) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: suggested, highlight: true }\"\n ></ng-container>\n }\n </mat-optgroup>\n }\n\n <!-- Selected items (multi) -->\n @if (valueManager.value()?.length && type() !== autocompleteType.SINGLE && !hideSelectedSection()) {\n <mat-optgroup [label]=\"(translationContext + 'SELECTED' | uiTranslate | async)! | uppercase\">\n @for (item of $any(valueManager.value()); track item) {\n <ng-container *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: false }\"></ng-container>\n }\n </mat-optgroup>\n }\n\n <!-- Main items (normalized groups) -->\n @if (virtualScroll() || infiniteScroll()) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"48\"\n [style.height.px]=\"getVirtualScrollHeight(flatItemsList().length)\"\n class=\"virtual-scroll\"\n >\n @if (!isGrouped) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n *cdkVirtualFor=\"let item of flatItemsList(); trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n } @else {\n @for (group of groupedItemsList(); track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{\n 'selected-option': item | includes: valueManager.value() ?? [] : valueField(),\n }\"\n *cdkVirtualFor=\"let item of group.items; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n </mat-optgroup>\n } @else {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n *cdkVirtualFor=\"let item of group.items; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n }\n }\n\n @if (infiniteScroll() && hasMoreOptionsForInfiniteScroll()) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n @if (isGrouped) {\n @for (group of groupedItemsList(); track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.items; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.items; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n }\n }\n } @else {\n @for (item of flatItemsList(); track item) {\n <ng-container *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"></ng-container>\n }\n }\n }\n\n <!-- Selected item (single) -->\n @if (valueManager.value()?.length && type() === autocompleteType.SINGLE && !hideSelectedSection()) {\n <mat-optgroup [label]=\"(translationContext + 'SELECTED' | uiTranslate | async)! | uppercase\">\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: valueManager.value(), highlight: true }\"\n ></ng-container>\n </mat-optgroup>\n }\n } @else {\n <!-- ── Dropdown (unfiltered) ────────────────────────────────── -->\n @if (virtualScroll() || infiniteScroll()) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"48\"\n [style.height.px]=\"getVirtualScrollHeight(flatItemsList().length)\"\n class=\"virtual-scroll\"\n >\n @if (!isGrouped) {\n <!-- Ungrouped: flat cdkVirtualFor directly in viewport -->\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n *cdkVirtualFor=\"let item of flatItemsList(); trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n } @else {\n <!-- Grouped: nested cdkVirtualFor per group -->\n @for (group of groupedItemsList(); track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{\n 'selected-option': item | includes: valueManager.value() ?? [] : valueField(),\n }\"\n *cdkVirtualFor=\"let item of group.items; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n </mat-optgroup>\n } @else {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n *cdkVirtualFor=\"let item of group.items; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n }\n }\n\n @if (infiniteScroll() && hasMoreOptionsForInfiniteScroll()) {\n <mat-option class=\"loading-option\" [style.pointer-events]=\"'none'\">\n <div class=\"loading-container\">\n <ui-spinner size=\"small\" [isLoader]=\"false\"></ui-spinner>\n </div>\n </mat-option>\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n @if (isGrouped) {\n @for (group of groupedItemsList(); track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.items; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.items; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n }\n }\n } @else {\n @for (item of flatItemsList(); track item) {\n <ng-container *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"></ng-container>\n }\n }\n }\n }\n\n @if (userAdditionsGroupLabel() && userAddedItems().length) {\n <mat-optgroup [label]=\"userAdditionsGroupLabel()\">\n @for (item of userAddedItems(); track item) {\n <ng-container *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"></ng-container>\n }\n </mat-optgroup>\n }\n }\n\n <!-- ── Filtered ────────────────────────────────────────────────── -->\n @if (filterService.filteredSuggestions$ | async; as filteredSuggestionList) {\n @if (filterService.filteredItems$ | async; as filteredItemsList) {\n @if (virtualScroll() || infiniteScroll()) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"48\"\n [style.height.px]=\"getVirtualScrollHeight(filteredItemsList.length)\"\n class=\"virtual-scroll\"\n >\n @if (addOptionPosition() === 'top') {\n <ng-container\n *ngTemplateOutlet=\"\n addOptionTpl;\n context: { items: filteredItemsList, suggestions: filteredSuggestionList }\n \"\n ></ng-container>\n }\n <!-- Filtered suggestions -->\n @if (filteredSuggestionList.length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n *cdkVirtualFor=\"let item of filteredSuggestionList; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n </mat-optgroup>\n }\n\n <!-- Filtered items: grouped -->\n @if (isGrouped && (filterService.filteredGroupedItems$ | async); as filteredGroups) {\n @if (filteredGroups.length) {\n @for (group of filteredGroups; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{\n 'selected-option': item | includes: valueManager.value() ?? [] : valueField(),\n }\"\n *cdkVirtualFor=\"let item of group.filteredItems; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n </mat-optgroup>\n } @else {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{\n 'selected-option': item | includes: valueManager.value() ?? [] : valueField(),\n }\"\n *cdkVirtualFor=\"let item of group.filteredItems; trackBy: trackByItem\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n }\n }\n }\n\n <!-- Filtered items: ungrouped (no suggestions) -->\n @if (!filteredSuggestionList.length && !isGrouped) {\n @for (item of filteredItemsList; let idx = $index; track item[valueField()] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n } @else {\n <!-- Filtered items: ungrouped with suggestions present (ALL_ITEMS group) -->\n @if (filteredItemsList.length && !isGrouped) {\n <mat-optgroup [label]=\"(translationContext + 'ALL_ITEMS' | uiTranslate | async)! | uppercase\">\n @for (item of filteredItemsList; let idx = $index; track item[valueField()] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{\n 'selected-option': item | includes: valueManager.value() ?? [] : valueField(),\n }\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n </mat-optgroup>\n }\n }\n\n @if (addOptionPosition() === 'bottom') {\n <ng-container\n *ngTemplateOutlet=\"\n addOptionTpl;\n context: { items: filteredItemsList, suggestions: filteredSuggestionList }\n \"\n ></ng-container>\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n @if (addOptionPosition() === 'top') {\n <ng-container\n *ngTemplateOutlet=\"\n addOptionTpl;\n context: { items: filteredItemsList, suggestions: filteredSuggestionList }\n \"\n ></ng-container>\n }\n <!-- Filtered items: grouped -->\n @if (isGrouped && (filterService.filteredGroupedItems$ | async); as filteredGroups) {\n @if (filteredGroups.length) {\n @for (group of filteredGroups; track group) {\n @if (group.groupName) {\n <mat-optgroup [label]=\"group.groupName\">\n @for (item of group.filteredItems; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n </mat-optgroup>\n } @else {\n @for (item of group.filteredItems; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n }\n }\n }\n }\n\n <!-- Filtered suggestions -->\n @if (filteredSuggestionList.length) {\n <mat-optgroup [label]=\"(translationContext + 'SUGGESTED' | uiTranslate | async)! | uppercase\">\n @for (item of filteredSuggestionList; track item) {\n <ng-container\n *ngTemplateOutlet=\"optionTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n }\n </mat-optgroup>\n }\n\n <!-- Filtered items: ungrouped (no suggestions) -->\n @if (!filteredSuggestionList.length && !isGrouped) {\n @for (item of filteredItemsList; let idx = $index; track item[valueField()] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n } @else {\n <!-- Filtered items: ungrouped with suggestions present (ALL_ITEMS group) -->\n @if (filteredItemsList.length && !isGrouped) {\n <mat-optgroup [label]=\"(translationContext + 'ALL_ITEMS' | uiTranslate | async)! | uppercase\">\n @for (item of filteredItemsList; let idx = $index; track item[valueField()] || idx) {\n <mat-option\n (mousedown)=\"$event.preventDefault()\"\n [ngClass]=\"{ 'selected-option': item | includes: valueManager.value() ?? [] : valueField() }\"\n [value]=\"item\"\n >\n <ng-container\n *ngTemplateOutlet=\"optionContentTpl; context: { item: item, highlight: true }\"\n ></ng-container>\n </mat-option>\n }\n </mat-optgroup>\n }\n }\n\n @if (addOptionPosition() === 'bottom') {\n <ng-container\n *ngTemplateOutlet=\"\n addOptionTpl;\n context: { items: filteredItemsList, suggestions: filteredSuggestionList }\n \"\n ></ng-container>\n }\n }\n }\n }\n </mat-autocomplete>\n @if (hasError()) {\n <mat-hint class=\"error\">\n @if (ngControl) {\n <ui-validation-error\n [ngControl]=\"ngControl\"\n [customErrors]=\"formErrorConfig()\"\n [label]=\"label()\"\n ></ui-validation-error>\n } @else {\n @for (error of safeErrors(); track error) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ error }}</span>\n </div>\n }\n }\n </mat-hint>\n }\n </mat-form-field>\n @if (tagsBelow() && valueManager.value()?.length) {\n <div class=\"selected-items--below\">\n @for (valueItem of $any(valueManager.value()); track valueItem[valueField()] || i; let i = $index) {\n <ui-tag\n class=\"value-tag\"\n [id]=\"'below-tag-' + i\"\n [label]=\"valueItem | transformItem: textField()\"\n [allowClose]=\"true\"\n [isDisabled]=\"disabled()\"\n [applicationTheme]=\"applicationTheme\"\n (close)=\"onOptionRemoved(valueItem)\"\n ></ui-tag>\n }\n <!-- Consumer content rendered as a sibling of the selected tags, before the clear button -->\n <ng-content select=\"[autocompleteTagsSuffix]\"></ng-content>\n <button type=\"button\" class=\"clear-all\" [disabled]=\"_isDisabled()\" (click)=\"onClear()\">\n {{ 'COMMON.CLEAR' | uiTranslate | async }}\n </button>\n </div>\n }\n </div>\n</ng-container>\n","queryGuide":{"roles":["option"],"ariaAttributes":["aria-live","aria-label"],"dataTestIds":["autocomplete-v2-input"],"materialComponents":["mat-form-field","mat-label","mat-autocomplete","mat-option","mat-optgroup","mat-hint"],"childUIComponents":["ui-tag","ui-icon","ui-spinner","ui-validation-error"],"suggestedQueries":["screen.getByRole('option')","screen.getByLabelText('label text')","screen.getByTestId('autocomplete-v2-input')"]},"filePath":"components/autocomplete-v2/autocomplete-v2.component.ts","supportsTheme":false,"lastModified":1786626756375.621},"avatar":{"name":"AvatarComponent","selector":"ui-avatar","category":"avatar","description":"Displays a user's identity as a circular image or initials fallback. Available in three sizes (small, medium, large) and three initials colour variants (grey, pink, blue). Used throughout the platform to represent candidates, hiring managers, and team members in tables, cards, side panels, and navigation.","importPath":"@testgorilla/tgo-ui/components/avatar","moduleName":"AvatarComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"AvatarSizeType","defaultValue":"AvatarSize.SMALL","required":false,"alias":null,"transform":null,"description":"Input property for specifying the size of the avatar.","isSignal":false},{"name":"imageUrl","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input property for specifying the URL of the image to display in the avatar.","isSignal":false},{"name":"name","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input property for specifying the initials to display in the avatar.","isSignal":false},{"name":"tooltipText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input property for specifying the tooltip text for edit.","isSignal":false},{"name":"allowEdit","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input property that makes avatar editable.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme where","isSignal":false},{"name":"backgroundColor","type":"BackgroundAvatarColorType","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Defines the background color","isSignal":false}],"outputs":[{"name":"editClick","eventType":"Event","alias":null,"description":"Event emitted when the edit button is clicked.","isSignal":false}],"module":{"name":"AvatarComponentModule","imports":["CommonModule","IconComponentModule","MatTooltipModule","ButtonComponentModule","UiTranslatePipe","AvatarComponent","NameInitialsPipe"],"exports":["AvatarComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"AvatarSizeType","value":"`${AvatarSize}`"},{"name":"BackgroundAvatarColorType","value":"'red' | 'blue' | 'green' | 'orange' | 'pink'"}],"interfaces":[],"enums":[{"name":"AvatarSize","members":[{"name":"SMALL","value":"'small'"},{"name":"SMALL_MEDIUM","value":"'smallMedium'"},{"name":"MEDIUM","value":"'medium'"},{"name":"MEDIUM_LARGE","value":"'mediumLarge'"},{"name":"LARGE","value":"'large'"}]},{"name":"AvatarBackgroundColor","members":[{"name":"PINK","value":"'#FFE6FA'"},{"name":"BLUE","value":"'#D9E8FF'"},{"name":"GREEN","value":"'#D4F2C3'"},{"name":"ORANGE","value":"'#FFE1B3'"},{"name":"RED","value":"'#FFAC9A'"}]}]},"examples":[{"name":"MultipleDefaultAvatar","template":"<ui-avatar [applicationTheme]=\"${appTheme}\" size=\"small\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"smallMedium\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"medium\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"mediumLarge\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"large\"></ui-avatar>","props":{}},{"name":"MultipleImageAvatar","template":"<ui-avatar [applicationTheme]=\"${appTheme}\" size=\"small\" imageUrl=\"/images/Avatar.png\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"smallMedium\" imageUrl=\"/images/Avatar.png\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"medium\" imageUrl=\"/images/Avatar.png\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"mediumLarge\" imageUrl=\"/images/Avatar.png\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"large\" imageUrl=\"/images/Avatar.png\"></ui-avatar>","props":{}},{"name":"MultipleInitialsAvatar","template":"<ui-avatar [applicationTheme]=\"${appTheme}\" [backgroundColor]=\"'RED'\" size=\"small\" name=\"LS\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"smallMedium\" name=\"LS\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"medium\" name=\"LS\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"mediumLarge\" name=\"LS\"></ui-avatar>\n <ui-avatar [applicationTheme]=\"${appTheme}\" size=\"large\" name=\"LS\"></ui-avatar>","props":{}}],"documentation":"Displays a user's identity as a circular image or initials fallback. Available in three sizes (small, medium, large) and three initials colour variants (grey, pink, blue). Used throughout the platform to represent candidates, hiring managers, and team members in tables, cards, side panels, and navigation.\n\n## When to use\n\n- **Initials fallback** when a profile photo isn't available — never leave the avatar empty\n- **The correct size for the context** — small for dense data tables, medium for cards and panels, large for profile headers\n- **The Multiple variant** when showing a group of up to 4 users in a compact stack\n\n## When NOT to use\n\n- **As a button** → don't use Avatar on its own; wrap it in an Icon Button or add explicit affordance\n- **Mixed sizes in the same horizontal group** → stacked avatars should all be the same size\n- **Pink or blue initials for generic users** → reserve colour variants for system-assigned roles or visual differentiation where meaning is established\n\n## Import\n\nTo use the Avatar component, you need to import the `AvatarComponentModule` in your module:\n\n```typescript\nimport { AvatarComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n AvatarComponentModule,\n // ...\n ],\n})\nexport class YourModule {}\n```\n\n## Template Example\n\n```html\n<!-- Default Avatar -->\n<ui-avatar></ui-avatar>\n\n<!-- Avatar with Image -->\n<ui-avatar imageUrl=\"/path/to/image.jpg\"></ui-avatar>\n\n<!-- Avatar with Initials -->\n<ui-avatar name=\"John Doe\"></ui-avatar>\n\n<!-- Customized Size -->\n<ui-avatar size=\"large\" name=\"John Doe\"></ui-avatar>\n\n<!-- Editable Avatar with Custom Background -->\n<ui-avatar\n size=\"large\"\n backgroundColor=\"red\"\n allowEdit=\"true\"\n tooltipText=\"Edit profile picture\"\n (editClick)=\"onEditClick($event)\"\n>\n</ui-avatar>\n```\n","whenToUse":["**Initials fallback** when a profile photo isn't available — never leave the avatar empty","**The correct size for the context** — small for dense data tables, medium for cards and panels, large for profile headers","**The Multiple variant** when showing a group of up to 4 users in a compact stack"],"whenNotToUse":["**As a button** → don't use Avatar on its own; wrap it in an Icon Button or add explicit affordance","**Mixed sizes in the same horizontal group** → stacked avatars should all be the same size","**Pink or blue initials for generic users** → reserve colour variants for system-assigned roles or visual differentiation where meaning is established"],"template":"<div\n class=\"avatar-container\"\n [attr.type]=\"!imageUrl && !name ? 'icon' : 'initials'\"\n [ngClass]=\"[size]\"\n [attr.theme]=\"applicationTheme\"\n [ngStyle]=\"{ backgroundColor: backgroundAvatarColor }\"\n>\n @if (name && !imageUrl) {\n <span class=\"initials\">\n {{ name | nameInitials }}\n </span>\n }\n @if (imageUrl) {\n <img [attr.aria-hidden]=\"true\" [attr.src]=\"imageUrl\" [alt]=\"\" (error)=\"handleImageError()\" />\n }\n @if (!imageUrl && !name) {\n <ui-icon\n [attr.aria-hidden]=\"true\"\n [color]=\"applicationTheme === 'classic' ? 'dark' : 'rebrand-black'\"\n [applicationTheme]=\"applicationTheme\"\n [size]=\"iconSize[size]\"\n [name]=\"'User-profile'\"\n ></ui-icon>\n }\n @if (allowEdit && (size === avatarSize.LARGE || size === avatarSize.MEDIUM_LARGE)) {\n <div class=\"edit-avatar\">\n <ui-button\n [applicationTheme]=\"applicationTheme\"\n [companyColor]=\"'black'\"\n [size]=\"'small'\"\n [iconName]=\"'Edit'\"\n [variant]=\"'icon-button'\"\n [tooltip]=\"tooltipText\"\n [ariaLabel]=\"('AVATAR.EDIT_AVATAR' | uiTranslate | async)!\"\n (buttonClickEvent)=\"onEditClick($event)\"\n ></ui-button>\n </div>\n }\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-hidden"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon","ui-button"],"suggestedQueries":[]},"filePath":"components/avatar/avatar.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=920-10424","lastModified":1786626756378.077},"badge":{"name":"BadgeComponent","selector":"ui-badge","category":"badge","description":"A small, pill-shaped label that communicates status, category, or count. Badges are read-only — they signal information, they don't trigger actions. Used across assessment results, candidate tables, and test library to show states like 'Passed', 'In progress', 'AI-generated', and score bands.","importPath":"@testgorilla/tgo-ui/components/badge","moduleName":"BadgeComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The label to be displayed on the badge.","isSignal":false},{"name":"staticLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A static label always displayed after the main label, never truncated.\nIntended for short suffixes (max 4 chars, e.g. \"100%\").\nWhen the main label is truncated, the tooltip shows both labels in full.","isSignal":false},{"name":"icon","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The icon name to be displayed on the badge.","isSignal":false},{"name":"color","type":"BadgeColor","defaultValue":"'petrol'","required":false,"alias":null,"transform":null,"description":"The color of the badge. Defaults to 'petrol'.","isSignal":false},{"name":"variant","type":"BadgeVariant","defaultValue":"'primary'","required":false,"alias":null,"transform":null,"description":"The variant of the badge. Defaults to 'primary'.","isSignal":false},{"name":"notificationsAmount","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The amount of notifications to be displayed on the badge.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"rebrandColor","type":"RebrandBadgeColor","defaultValue":"'brand'","required":false,"alias":null,"transform":null,"description":"The color of the badge on new theme. Defaults to 'brand'.","isSignal":false},{"name":"truncateLabel","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"enableAnimation","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether to enable the animation when label changes from empty to having content.","isSignal":false},{"name":"iconColor","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"Override color for the leading icon. Accepts any value the `ui-icon`\n`color` input accepts (named token, hex, or CSS custom property).\nWhen unset, falls back to the auto-derived color based on the badge background.","isSignal":true},{"name":"trailingIcon","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Optional trailing icon, rendered after the label/static label on the primary variant.","isSignal":true},{"name":"trailingIconColor","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"Override color for the trailing icon. Accepts any value the `ui-icon`\n`color` input accepts (named token, hex, or CSS custom property).\nWhen unset, falls back to the auto-derived color based on the badge background.","isSignal":true}],"outputs":[],"module":{"name":"BadgeComponentModule","imports":["CommonModule","IconComponentModule","MatTooltipModule","BadgeComponent"],"exports":["BadgeComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"BadgeVariant","value":"'primary' | 'secondary' | 'notification' | 'primary-ai' | 'secondary-ai'"},{"name":"BadgeColor","value":"| 'red'\n | 'grey-medium'\n | 'teal'\n | 'yellow'\n | 'petrol'\n | 'teal-60w'\n | 'petrol-90w'\n | 'petrol-60w'\n | 'grey-dark'\n | 'blue-20'\n | 'blue-40'\n | 'pink-30'\n | 'blue-10'"},{"name":"RebrandBadgeColor","value":"| 'success'\n | 'warning'\n | 'negative'\n | 'info'\n | 'brand'\n | 'neutral'\n | 'blue-20'\n | 'blue-40'\n | 'pink-30'\n | 'blue-10'\n | 'grey-10'\n | 'grey-20'\n | 'grey-30'"}],"interfaces":[],"enums":[{"name":"BadgeColorEnum","members":[{"name":"RED","value":"'red'"},{"name":"GREY_MEDIUM","value":"'grey-medium'"},{"name":"TEAL","value":"'teal'"},{"name":"YELLOW","value":"'yellow'"},{"name":"PETROL","value":"'petrol'"},{"name":"TEAL_60W","value":"'teal-60w'"},{"name":"PETROL_90W","value":"'petrol-90w'"},{"name":"PETROL_60W","value":"'petrol-60w'"},{"name":"GREY_DARK","value":"'grey-dark'"},{"name":"BLUE_20","value":"'blue-20'"},{"name":"BLUE_40","value":"'blue-40'"}]},{"name":"RebrandBadgeColorEnum","members":[{"name":"SUCCESS","value":"'success'"},{"name":"WARNING","value":"'warning'"},{"name":"NEGATIVE","value":"'negative'"},{"name":"INFO","value":"'info'"},{"name":"BRAND","value":"'brand'"},{"name":"NEUTRAL","value":"'neutral'"},{"name":"BLUE_40","value":"'blue-40'"},{"name":"BLUE_20","value":"'blue-20'"},{"name":"PINK_30","value":"'pink-30'"},{"name":"BLUE_10","value":"'blue-10'"},{"name":"GREY_30","value":"'grey-30'"},{"name":"GREY_20","value":"'grey-20'"},{"name":"GREY_10","value":"'grey-10'"}]},{"name":"BadgeVariantEnum","members":[{"name":"PRIMARY","value":"'primary'"},{"name":"PRIMARY_AI","value":"'primary-ai'"},{"name":"SECONDARY","value":"'secondary'"},{"name":"SECONDARY_AI","value":"'secondary-ai'"},{"name":"NOTIFICATION","value":"'notification'"}]}]},"examples":[],"documentation":"A small, pill-shaped label that communicates status, category, or count. Badges are read-only — they signal information, they don't trigger actions. Used across assessment results, candidate tables, and test library to show states like 'Passed', 'In progress', 'AI-generated', and score bands.\n\n## When to use\n\n- **Match the semantic colour to the meaning** — success (green) for positive outcomes, error (red) for failed or blocked, warning (amber) for in-progress or at-risk, info (blue) for neutral status\n- **Reinforce meaning with the icon variant** when the label alone might be ambiguous in a dense table\n- **Reserve the AI variant for AI-generated content labels** — it's a brand signal, not a general-purpose colour\n\n## When NOT to use\n\n- **More than one primary badge per row in a data table** → it creates visual noise and dilutes meaning\n- **As a CTA or clickable affordance** → use a Button or Tag instead\n- **New colour combinations** → only use the defined semantic variants\n\n## How to use\n\nTo use the Badge component, you need to import the `BadgeComponentModule` in your module:\n\n```typescript\nimport { BadgeComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n BadgeComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-badge label=\"Hello\" icon=\"Info\" color=\"petrol\" variant=\"primary\" applicationTheme=\"light\"> </ui-badge>\n```\n","whenToUse":["**Match the semantic colour to the meaning** — success (green) for positive outcomes, error (red) for failed or blocked, warning (amber) for in-progress or at-risk, info (blue) for neutral status","**Reinforce meaning with the icon variant** when the label alone might be ambiguous in a dense table","**Reserve the AI variant for AI-generated content labels** — it's a brand signal, not a general-purpose colour"],"whenNotToUse":["**More than one primary badge per row in a data table** → it creates visual noise and dilutes meaning","**As a CTA or clickable affordance** → use a Button or Tag instead","**New colour combinations** → only use the defined semantic variants"],"template":"<div\n [class]=\"'badge badge-container-' + variant\"\n [class.no-label]=\"!label && !staticLabel && !trailingIcon()\"\n [attr.aria-label]=\"label + (staticLabel ? ' ' + staticLabel : '')\"\n [attr.theme]=\"applicationTheme\"\n [attr.color]=\"isClassicTheme ? color : rebrandColor\"\n [matTooltip]=\"\n label.length > 25 - (staticLabel.length < 4 ? staticLabel.length : 4)\n ? label + (staticLabel ? ' ' + staticLabel : '')\n : ''\n \"\n [matTooltipClass]=\"applicationTheme\"\n>\n @switch (variant) {\n @case (badgeVariant.PRIMARY) {\n @if (icon) {\n <ui-icon\n [class.badge-icon]=\"!!label || !!staticLabel || !!trailingIcon\"\n [name]=\"icon\"\n [color]=\"resolvedIconColor\"\n ></ui-icon>\n }\n }\n @case (badgeVariant.PRIMARY_AI) {\n <ng-container [ngTemplateOutlet]=\"aiIconTpl\"></ng-container>\n }\n @case (badgeVariant.SECONDARY_AI) {\n <ng-container [ngTemplateOutlet]=\"aiIconTpl\"></ng-container>\n }\n @case (badgeVariant.SECONDARY) {\n <div class=\"point\" [attr.color]=\"isClassicTheme ? color : rebrandColor\"></div>\n }\n @case (badgeVariant.NOTIFICATION) {\n <div class=\"number-notification\">{{ notificationsAmount }}</div>\n }\n }\n @if (variant !== badgeVariant.NOTIFICATION) {\n <div class=\"label\" [class.empty]=\"!label && !staticLabel\" [class.animated]=\"enableAnimation\">\n {{\n label.length > 25 - (staticLabel.length < 4 ? staticLabel.length : 4) && truncateLabel\n ? (label | slice: 0 : 25 - (staticLabel.length < 4 ? staticLabel.length : 4)) + '...'\n : label\n }}\n </div>\n }\n @if (staticLabel && variant !== badgeVariant.NOTIFICATION) {\n <span class=\"static-label\">{{ staticLabel | slice: 0 : 4 }}</span>\n }\n @if (trailingIcon() && variant === badgeVariant.PRIMARY) {\n <ui-icon class=\"badge-icon-trailing\" [name]=\"trailingIcon()\" [color]=\"resolvedTrailingIconColor\"></ui-icon>\n }\n</div>\n\n<ng-template #aiIconTpl>\n <ui-icon class=\"badge-icon\" [name]=\"'Sparkle-in-line'\" [color]=\"'ai'\"></ui-icon>\n</ng-template>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByLabelText('label text')"]},"colorSwatches":[{"input":"color","type":"BadgeColor","appliesWhen":"applicationTheme \"classic\" only (legacy theme)","variantNotes":{"primary":"color fills the whole pill background; foreground is the label/icon color on that fill","primary-ai":"same fill as primary; the leading AI sparkle icon keeps its brand gradient","secondary":"color fills only the small leading dot — the label is always black (#000000) on a transparent background","secondary-ai":"like secondary, with the AI sparkle icon instead of the dot","notification":"counter circle uses the color as background with the foreground listed per swatch"},"swatches":[{"value":"red","background":"#cb7b7a","foreground":"#ffffff","backgroundToken":"$tgo-error-default"},{"value":"grey-medium","background":"#e0e0e0","foreground":"#000000","backgroundToken":"$tgo-medium"},{"value":"teal","background":"#46a997","foreground":"#ffffff","backgroundToken":"$tgo-teal-default"},{"value":"yellow","background":"#cca45f","foreground":"#ffffff","backgroundToken":"$tgo-warning-default"},{"value":"petrol","background":"#276678","foreground":"#ffffff","backgroundToken":"$tgo-petrol-default"},{"value":"teal-60w","background":"#b5ddd5","foreground":"#000000","backgroundToken":"$tgo-teal-60w"},{"value":"petrol-90w","background":"#e9f0f1","foreground":"#000000","backgroundToken":"$tgo-petrol-90w"},{"value":"petrol-60w","background":"#a9c2c9","foreground":"#000000","backgroundToken":"$tgo-petrol-60w"},{"value":"grey-dark","background":"#888888","foreground":"#ffffff","backgroundToken":"$tgo-dark"},{"value":"blue-20","background":"#99c1fe","foreground":"#242424","backgroundToken":"$informative-20"},{"value":"blue-40","background":"#0165fc","foreground":"#ffffff","backgroundToken":"$informative-40"},{"value":"pink-30","background":"#ffb3ee","foreground":"#242424","backgroundToken":"$brand-30"},{"value":"blue-10","background":"#d9e8ff","foreground":"#242424","backgroundToken":"$informative-10"}]},{"input":"rebrandColor","type":"RebrandBadgeColor","appliesWhen":"applicationTheme \"light\" | \"dark\" (the default themes)","variantNotes":{"primary":"color fills the whole pill background; foreground is the label/icon color on that fill","primary-ai":"same fill as primary; the leading AI sparkle icon keeps its brand gradient","secondary":"color fills only the small leading dot — the label is always black (#000000) on a transparent background","secondary-ai":"like secondary, with the AI sparkle icon instead of the dot","notification":"ignores this input on light/dark themes — the counter circle is always #242424 with #ffffff text"},"swatches":[{"value":"success","background":"#d4f2c3","foreground":"#242424","backgroundToken":"$success-10"},{"value":"warning","background":"#ffe1b3","foreground":"#242424","backgroundToken":"$warning-20"},{"value":"negative","background":"#ffac9a","foreground":"#242424","backgroundToken":"$error-20"},{"value":"info","background":"#d9e8ff","foreground":"#242424","backgroundToken":"$informative-10"},{"value":"brand","background":"#ffe6fa","foreground":"#242424","backgroundToken":"$brand-20"},{"value":"neutral","background":"#e9e9e9","foreground":"#242424","backgroundToken":"$grayscale-20"},{"value":"blue-20","background":"#99c1fe","foreground":"#242424","backgroundToken":"$informative-20"},{"value":"blue-40","background":"#0165fc","foreground":"#ffffff","backgroundToken":"$informative-40"},{"value":"pink-30","background":"#ffb3ee","foreground":"#242424","backgroundToken":"$brand-30"},{"value":"blue-10","background":"#d9e8ff","foreground":"#242424","backgroundToken":"$informative-10"},{"value":"grey-10","background":"#f4f4f4","foreground":"#242424","backgroundToken":"$grayscale-10"},{"value":"grey-20","background":"#e9e9e9","foreground":"#242424","backgroundToken":"$grayscale-20"},{"value":"grey-30","background":"#d3d3d3","foreground":"#242424","backgroundToken":"$grayscale-30"}]}],"filePath":"components/badge/badge.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=77-4082","lastModified":1786626756378.9983},"breadcrumb":{"name":"BreadcrumbComponent","selector":"ui-breadcrumb","category":"breadcrumb","description":"Shows the user's current location within the product hierarchy and lets them jump back to parent pages. Automatically handles overflow by collapsing intermediate items into a dropdown when space is tight, and truncates long labels intelligently. Used in nested flows like assessment setup, candidate results, and account settings.","importPath":"@testgorilla/tgo-ui/components/breadcrumb","moduleName":"BreadcrumbComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"BreadcrumbSize","defaultValue":"'large'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"items","type":"BreadcrumbItem[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"includeBackButton","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"isLoading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"numberOfLoadingItems","type":"number","defaultValue":"3","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxItemLength","type":"number","defaultValue":"18","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"selectItem","eventType":"string","alias":null,"description":"","isSignal":false},{"name":"backButtonClickEvent","eventType":"Event","alias":null,"description":"","isSignal":false}],"module":{"name":"BreadcrumbComponentModule","imports":["CommonModule","IconComponentModule","MatTooltipModule","OverflowMenuComponentModule","ButtonComponentModule","SkeletonComponent","UiTranslatePipe","TruncatePipe","BreadcrumbComponent"],"exports":["BreadcrumbComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"BreadcrumbSize","value":"'small' | 'medium' | 'large'"}],"interfaces":[{"name":"BreadcrumbItem","properties":[{"name":"label","type":"string","optional":false},{"name":"value","type":"string","optional":false}]}],"enums":[]},"examples":[],"documentation":"Shows the user's current location within the product hierarchy and lets them jump back to parent pages. Automatically handles overflow by collapsing intermediate items into a dropdown when space is tight, and truncates long labels intelligently. Used in nested flows like assessment setup, candidate results, and account settings.\n\n## When to use\n\n- **Mark the current page** — always include it as the last, non-clickable item so users know exactly where they are\n- **Large for page headers, Medium for embedded breadcrumbs** — use Large for standalone page headers and Medium within content sections\n- **Include Back Button variant** — use it in modal flows or side panels where the browser back button isn't available\n\n## When NOT to use\n\n- **Top-level pages** → don't use Breadcrumb where there's no meaningful parent, as an orphaned breadcrumb adds nothing\n- **Manual label truncation** → don't override it; the component handles truncation automatically and overriding it breaks overflow behaviour\n- **Paths deeper than 4 levels** → don't exceed 4 levels; if navigation is that deep, reconsider the IA\n\n## How to use\n\nTo use the Breadcrumb component, you need to import the `BreadcrumbComponentModule` in your module:\n\n```typescript\nimport { BreadcrumbComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n BreadcrumbComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-breadcrumb\n [items]=\"breadcrumbItems\"\n [size]=\"'large'\"\n [applicationTheme]=\"'light'\"\n (selectItem)=\"onBreadcrumbItemClick($event)\"\n>\n</ui-breadcrumb>\n```\n\nFor component setup:\n\n```typescript\nimport { BreadcrumbItem } from '@testgorilla/tgo-ui';\n\nexport class MyComponent {\n breadcrumbItems: BreadcrumbItem[] = [\n { label: 'Home', value: 'home' },\n { label: 'Products', value: 'products' },\n { label: 'Category', value: 'category' },\n { label: 'Current Page', value: 'current-page' },\n ];\n\n onBreadcrumbItemClick(value: string): void {\n // Handle navigation logic\n console.log('Breadcrumb item clicked:', value);\n }\n}\n```\n\nDifferent sizes:\n\n```html\n<ui-breadcrumb [items]=\"items\" [size]=\"'small'\"></ui-breadcrumb>\n<ui-breadcrumb [items]=\"items\" [size]=\"'medium'\"></ui-breadcrumb>\n<ui-breadcrumb [items]=\"items\" [size]=\"'large'\"></ui-breadcrumb>\n```\n\nThe breadcrumb component automatically handles overflow by moving items to a dropdown menu when space is limited, and provides intelligent truncation for long labels.\n","whenToUse":["**Mark the current page** — always include it as the last, non-clickable item so users know exactly where they are","**Large for page headers, Medium for embedded breadcrumbs** — use Large for standalone page headers and Medium within content sections","**Include Back Button variant** — use it in modal flows or side panels where the browser back button isn't available"],"whenNotToUse":["**Top-level pages** → don't use Breadcrumb where there's no meaningful parent, as an orphaned breadcrumb adds nothing","**Manual label truncation** → don't override it; the component handles truncation automatically and overriding it breaks overflow behaviour","**Paths deeper than 4 levels** → don't exceed 4 levels; if navigation is that deep, reconsider the IA"],"template":"@if (!isLoading()) {\n <div class=\"breadcrumb\" [attr.theme]=\"applicationTheme()\" #breadcrumbContainer [class]=\"'breadcrumb-' + size()\">\n @if (includeBackButton()) {\n <ui-button\n [variant]=\"'icon-button'\"\n [tooltip]=\"('COMMON.BACK' | uiTranslate | async)!\"\n [iconName]=\"'Arrow-chevron-left-filled'\"\n (buttonClickEvent)=\"backButtonClickEvent.emit($event)\"\n ></ui-button>\n }\n @if (isOverflowing()) {\n <ui-overflow-menu\n [applicationTheme]=\"applicationTheme()\"\n [contentTemplateRef]=\"overflowMenu\"\n [buttons]=\"overflowButtonItems()\"\n [matTooltip]=\"('COMMON.OPTIONS' | uiTranslate | async)!\"\n [matTooltipClass]=\"applicationTheme()\"\n [isDynamicMenu]=\"true\"\n (selectItem)=\"selectItem.emit($event)\"\n class=\"breadcrumb-overflow-menu\"\n ></ui-overflow-menu>\n <ui-icon name=\"Arrow-chevron-right-filled\" [applicationTheme]=\"applicationTheme()\" color=\"gray\"></ui-icon>\n }\n @for (item of visibleItems(); track item.value) {\n @if (!$last) {\n <span\n class=\"breadcrumb-item\"\n tabindex=\"0\"\n [matTooltip]=\"item.label.length > maxItemLength() ? item.label : ''\"\n [matTooltipClass]=\"applicationTheme()\"\n [attr.aria-label]=\"item.label\"\n (click)=\"selectItem.emit(item.value)\"\n (keydown.enter)=\"selectItem.emit(item.value)\"\n >\n {{ item.label | truncate: maxItemLength() : '...' }}\n </span>\n <ui-icon name=\"Arrow-chevron-right-filled\" [applicationTheme]=\"applicationTheme()\" color=\"gray\"></ui-icon>\n } @else {\n <strong\n class=\"breadcrumb-item-active\"\n tabindex=\"0\"\n [matTooltip]=\"shouldTruncateLastItem() ? item.label : ''\"\n [matTooltipClass]=\"applicationTheme()\"\n [class.breadcrumb-item-active-truncated]=\"shouldTruncateLastItem()\"\n [attr.aria-label]=\"item.label\"\n [attr.aria-current]=\"'page'\"\n (click)=\"selectItem.emit(item.value)\"\n (keydown.enter)=\"selectItem.emit(item.value)\"\n >\n {{ item.label }}\n </strong>\n }\n }\n </div>\n} @else {\n <div class=\"breadcrumb\">\n @for (i of loadingItems(); track $index) {\n <ui-skeleton count=\"1\" [theme]=\"{ width: '160px', margin: '10px 0 0' }\"></ui-skeleton>\n @if ($index !== numberOfLoadingItems() - 1) {\n <ui-icon name=\"Arrow-chevron-right-filled\" [applicationTheme]=\"applicationTheme()\" color=\"gray\"></ui-icon>\n }\n }\n </div>\n}\n<ng-template #overflowMenu>\n <div class=\"breadcrumb-item breadcrumb-overflow\" [class]=\"'breadcrumb-' + size()\">...</div>\n</ng-template>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label","aria-current"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-button","ui-overflow-menu","ui-icon","ui-skeleton"],"suggestedQueries":["screen.getByLabelText('label text')"]},"filePath":"components/breadcrumb/breadcrumb.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=18745-46591","lastModified":1786626756379.7126},"button":{"name":"ButtonComponent","selector":"ui-button","category":"button","description":"The primary interactive control for triggering actions. Follows a strict hierarchy: one primary action per screen, supported by secondary, ghost, and outlined variants for lower-priority actions. Destructive and icon-only variants cover delete flows and compact action bars. Used everywhere in the product — from assessment creation CTAs to table row actions.","importPath":"@testgorilla/tgo-ui/components/button","moduleName":"ButtonComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"ButtonSize","defaultValue":"'big'","required":false,"alias":null,"transform":null,"description":"Define button height","isSignal":false},{"name":"variant","type":"ButtonColor","defaultValue":"'primary'","required":false,"alias":null,"transform":null,"description":"Background color of the button while in active state","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Text content to appear on the button","isSignal":false},{"name":"iconPosition","type":"ButtonIconPosition","defaultValue":"'right'","required":false,"alias":null,"transform":null,"description":"Positioning of the icon (when existant)","isSignal":false},{"name":"justIcon","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Define if button has only icon without text","isSignal":false},{"name":"iconName","type":"IconName | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Name of ui-icon","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator if the button should be disabled","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator if the loading icon should be shown","isSignal":false},{"name":"loadingWithLabel","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show a small inline spinner to the left of the label while keeping the\nlabel visible. Pair with `disabled` to make the button non-interactive.\nIf `loading` is also true, `loading` takes precedence.","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator of the button width","isSignal":false},{"name":"url","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Url of button if it is a link","isSignal":false},{"name":"urlTarget","type":"LinkUrlTarget","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Url's target of button if it is a link","isSignal":false},{"name":"value","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Button value","isSignal":false},{"name":"tooltip","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"isPremium","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"type","type":"ButtonType","defaultValue":"'button'","required":false,"alias":null,"transform":null,"description":"The type of the button.","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the button.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"buttonBadgeConfig","type":"ButtonBadgeConfig | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Config for the button badge variation","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme where buttons is used","isSignal":false},{"name":"disabledScaleOnClick","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Disabled button scale on click","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaExpanded","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Whether the button controls a disclosure widget that is currently expanded.\nSet to `true` for an expanded disclosure, `false` for collapsed, or leave\nundefined when the button is not a disclosure trigger.","isSignal":false},{"name":"ariaControls","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The id of the element controlled by this button (used together with\n`ariaExpanded` for disclosure semantics).","isSignal":false},{"name":"ariaPressed","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Whether the button is a toggle button currently in the pressed state.\nUse `true` / `false` for two-state toggles. Leave undefined when the\nbutton is not a toggle.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaLabelledby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"preventDefault","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Prevents default button click behavior","isSignal":false},{"name":"hasBackground","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Ghost variant with white background","isSignal":false},{"name":"tooltipPosition","type":"TooltipPosition","defaultValue":"'below'","required":false,"alias":null,"transform":null,"description":"Sets tooltip position","isSignal":false},{"name":"role","type":"unknown","defaultValue":"'button'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"iconFilled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[{"name":"buttonClickEvent","eventType":"Event","alias":null,"description":"","isSignal":false},{"name":"buttonHoverEvent","eventType":"Event","alias":null,"description":"","isSignal":false}],"module":{"name":"ButtonComponentModule","imports":["CommonModule","MatButtonModule","IconComponentModule","MatProgressSpinnerModule","MatTooltipModule","BadgeComponentModule","EllipseTextDirective","MatRadioModule","UiTranslatePipe","ButtonComponent"],"exports":["ButtonComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"ButtonIconPosition","value":"'left' | 'right'"},{"name":"ButtonColor","value":"| 'primary'\n | 'secondary'\n | 'secondary-inverted'\n | 'ghost'\n | 'ghost-ai'\n | 'outlined'\n | 'destructive'\n | 'tertiary'\n | 'link'\n | 'tab'\n | 'text'\n | 'text-inline'\n | 'menuTrigger'\n | 'menuCell'\n | 'icon-button'\n | 'linkedin'"},{"name":"ButtonSize","value":"'big' | 'medium' | 'small'"},{"name":"LinkUrlTarget","value":"'' | '_self' | '_blank' | '_parent' | '_top'"},{"name":"IconButtonSize","value":"'24' | '32' | '40'"},{"name":"ButtonType","value":"'button' | 'submit' | 'reset'"},{"name":"ButtonState","value":"'hover' | 'pressed' | 'disabled' | 'default'"}],"interfaces":[{"name":"ButtonBadgeConfig","properties":[{"name":"badgeType","type":"BadgeVariant | null","optional":false},{"name":"badgeLabel","type":"string","optional":true},{"name":"badgeNotificationAmount","type":"number","optional":false},{"name":"rebrandColor","type":"RebrandBadgeColor","optional":true}]}],"enums":[]},"examples":[{"name":"ButtonOnlyLabel","template":"<div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary-inverted\" label=\"Secondary Inverted\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" iconName=\"Book\" variant=\"destructive\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" label=\"Tertiary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tab\" label=\"Tab\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" label=\"Text\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text-inline\" label=\"Text-inline\"></ui-button></div>\n </div>","props":{}},{"name":"ButtonAsLink","template":"<div>\n <p>Big size</p>\n Sample sentance with <ui-button applicationTheme=\"${appTheme}\" variant=\"link\" urlTarget=\"_blank\" href=\"https://www.testgorilla.com/\" label=\"Large\"></ui-button> link</div>\n <hr>\n <div>\n <p>Medium size</p>\n Sample sentance with <ui-button applicationTheme=\"${appTheme}\" size=\"medium\" variant=\"link\" urlTarget=\"_blank\" href=\"https://www.testgorilla.com/\" label=\"Medium\"></ui-button> link\n <hr>\n <p>Small size</p>\n Sample sentance with <ui-button applicationTheme=\"${appTheme}\" size=\"small\" variant=\"link\" urlTarget=\"_blank\" href=\"https://www.testgorilla.com/\" label=\"Small\"></ui-button> link\n <hr>\n </div>","props":{}},{"name":"Disabled","template":"<div class=\"row\"><div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" iconName=\"Book\" variant=\"primary\" [disabled]=\"true\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" iconName=\"Book\" [disabled]=\"true\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" iconName=\"Book\" [disabled]=\"true\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"destructive\" iconName=\"Book\" [disabled]=\"true\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" iconName=\"Book\" [disabled]=\"true\" label=\"Tertiary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tab\" iconName=\"Book\" [disabled]=\"true\" label=\"Tab\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" iconName=\"Book\" [disabled]=\"true\" label=\"Text\"></ui-button></div></div>\n <h4>Only icon</h4>\n <div class=\"row\">\n <div class=\"colum\"> <ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"destructive\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tab\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" [disabled]=\"true\" [justIcon]=\"true\" iconName=\"Test\"></ui-button></div></div>","props":{}},{"name":"LabelIconLeft","template":"<div class=\"row\"><div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" iconName=\"Book\" iconPosition=\"left\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" iconName=\"Book\" iconPosition=\"left\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" iconName=\"Book\" iconPosition=\"left\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost-ai\" iconName=\"Sparkle\" iconPosition=\"left\" label=\"Ghost AI\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"destructive\" iconName=\"Book\" iconPosition=\"left\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" iconName=\"Book\" iconPosition=\"left\" label=\"Tertiary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tab\" iconName=\"Book\" iconPosition=\"left\" label=\"Tab\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" iconName=\"Book\" iconPosition=\"left\" label=\"Text\"></ui-button></div></div>","props":{"iconPosition":"'left'"}},{"name":"LabelIconRight","template":"<div class=\"row\"><div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" iconName=\"Close\" iconPosition=\"right\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" iconName=\"Close\" iconPosition=\"right\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" iconName=\"Close\" iconPosition=\"right\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost-ai\" iconName=\"Sparkle\" iconPosition=\"right\" label=\"Ghost AI\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"destructive\" iconName=\"Close\" iconPosition=\"right\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" iconName=\"Close\" iconPosition=\"right\" label=\"Tertiary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tab\" iconName=\"Close\" iconPosition=\"right\" label=\"Tab\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" iconName=\"Close\" iconPosition=\"right\" label=\"Text\"></ui-button></div></div>","props":{"iconPosition":"'right'"}},{"name":"OnlyIcon","template":"<div class=\"row\"><div class=\"colum\">\n <ui-button variant=\"primary\" [justIcon]=\"true\" applicationTheme=\"${appTheme}\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"secondary\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"ghost\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"destructive\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"tertiary\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"tab\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"text\" applicationTheme=\"${appTheme}\" [justIcon]=\"true\" iconName=\"Book\"></ui-button></div></div>","props":{"justIcon":"true","iconName":"'Book'"}},{"name":"Loading","template":"<div class=\"row\"><div class=\"colum\">\n <ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"secondary\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"destructive\" iconName=\"Test\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"text\" ${argsToTemplate(args)}></ui-button></div></div>","props":{"loading":"true"}},{"name":"LoadingWithLabel","template":"<div class=\"label\">Disabled</div>\n <div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" label=\"Primary\" [disabled]=\"true\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" label=\"Ghost\" [disabled]=\"true\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" label=\"Tertiary\" [disabled]=\"true\" ${argsToTemplate(args)}></ui-button></div>\n </div>\n <div class=\"label\">Enabled</div>\n <div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"primary\" label=\"Primary\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"ghost\" label=\"Ghost\" ${argsToTemplate(args)}></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"tertiary\" label=\"Tertiary\" ${argsToTemplate(args)}></ui-button></div>\n </div>","props":{"loadingWithLabel":"true"}},{"name":"Small","template":"<div class=\"row\"><div class=\"colum\">\n <ui-button variant=\"primary\" size=\"small\" applicationTheme=\"${appTheme}\" [loading]=\"true\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"secondary\" label=\"Secondary\" iconName=\"Add\" [justIcon]=\"true\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"ghost\" iconName=\"Close\" iconPosition=\"right\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"destructive\" iconName=\"Devices\" iconPosition=\"left\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"tertiary\" label=\"Tertiary\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"tab\" label=\"Tab\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"text\" label=\"Text\"></ui-button></div>\n <div class=\"colum\"><ui-button size=\"small\" applicationTheme=\"${appTheme}\" variant=\"text-inline\" label=\"Text-inline\"></ui-button></div></div>","props":{"size":"'small'"}},{"name":"FullWidth","template":"<div class=\"row\">\n <ui-button variant=\"primary\" applicationTheme=\"${appTheme}\" [fullWidth]=\"true\" label=\"Primary\"></ui-button>\n <ui-button variant=\"secondary\" applicationTheme=\"${appTheme}\" [fullWidth]=\"true\" iconName=\"Devices\" iconPosition=\"right\" label=\"Secondary\"></ui-button>\n <ui-button variant=\"ghost\" applicationTheme=\"${appTheme}\" [fullWidth]=\"true\" iconName=\"Close\" iconPosition=\"left\" label=\"Ghost\"></ui-button></div>","props":{"fullWidth":"true"}},{"name":"MenuTrigger","template":"<div class=\"row\">\n <div class=\"colum\"><ui-button variant=\"menuTrigger\" [justIcon]=\"true\" iconName=\"Settings\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"menuTrigger\" [justIcon]=\"true\" iconName=\"Menu-ellipsis\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"menuTrigger\" [justIcon]=\"true\" iconName=\"Folder\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"menuTrigger\" [justIcon]=\"true\" iconName=\"Plus\"></ui-button></div></div>","props":{"variant":"'menuTrigger'","justIcon":"true","iconName":"'Settings'"}},{"name":"MenuCell","template":"<div class=\"row\">\n <div class=\"colum\"><ui-button variant=\"menuCell\" iconPosition=\"left\" iconName=\"Copy\" label=\"Copy\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"menuCell\" iconPosition=\"left\" iconName=\"Edit\" label=\"Edit\"></ui-button></div>\n <div class=\"colum\"><ui-button variant=\"menuCell\" iconPosition=\"left\" iconName=\"Delete\" label=\"Delete\"></ui-button></div></div>","props":{}},{"name":"IconButton","template":"<div>\n <h4>Enabled: </h4>\n <ui-button ${argsToTemplate(args)} applicationTheme=\"${appTheme}\"></ui-button>\n </div>\n <div>\n <h4>Disabled: </h4>\n <ui-button ${argsToTemplate(args)} applicationTheme=\"${appTheme}\" disabled=\"true\"></ui-button>\n </div>","props":{"variant":"'icon-button'","tooltip":"'Icon Button Tooltip'","iconName":"'Settings'"}},{"name":"PremiumButton","template":"<div class=\"row\"><div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} variant=\"primary\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} variant=\"secondary\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} variant=\"ghost\" label=\"Ghost\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} iconName=\"Test\" variant=\"destructive\" label=\"Destructive\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} variant=\"icon-button\" [justIcon]=\"true\" iconName=\"Book\" tooltip=\"Premium Icon Button\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" ${argsToTemplate(args)} variant=\"icon-button\" [justIcon]=\"true\" [disabled]=\"true\" iconName=\"Book\" tooltip=\"Premium Icon Button Disabled\"></ui-button></div>","props":{"isPremium":"true"}},{"name":"BadgeButton","template":"<div class=\"row\"><div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" [buttonBadgeConfig]='{\"badgeType\": \"primary\", \"badgeLabel\": \"Primary\"}' variant=\"primary\" label=\"Primary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" [buttonBadgeConfig]='{\"badgeType\": \"primary\", \"badgeLabel\": \"Primary\"}' variant=\"secondary\" label=\"Secondary\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" [buttonBadgeConfig]='{\"badgeType\": \"notification\", \"badgeNotificationAmount\": \"9\"}' variant=\"ghost\" label=\"Ghost\"></ui-button></div>","props":{"buttonBadgeConfig":"{","badgeType":"'primary'","badgeLabel":"'Primary'","badgeNotificationAmount":"0"}},{"name":"LongTruncatedLabel","template":"<div class=\"row\">\n <div class=\"colum\">\n <ui-button\n applicationTheme=\"${appTheme}\"\n [buttonBadgeConfig]='{\"badgeLabel\": \"Primary\"}'\n variant=\"primary\"\n ${argsToTemplate(args)}\n >\n\n </ui-button>\n </div>\n </div>","props":{"label":"'Some very long label that should be multilined and truncated to test the tooltip'"}},{"name":"LinkedIn","template":"<h4>Default</h4>\n <div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"linkedin\" size=\"small\" iconName=\"Linkedin-brand\" iconPosition=\"left\" label=\"Add to profile\"></ui-button></div>\n </div>\n <h4>Disabled</h4>\n <div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"linkedin\" size=\"small\" iconName=\"Linkedin-brand\" iconPosition=\"left\" [disabled]=\"true\" label=\"Add to profile\"></ui-button></div>\n </div>\n <h4>Big size</h4>\n <div class=\"row\">\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"linkedin\" size=\"big\" iconName=\"Linkedin-brand\" iconPosition=\"left\" label=\"Add to profile\"></ui-button></div>\n <div class=\"colum\"><ui-button applicationTheme=\"${appTheme}\" variant=\"linkedin\" size=\"big\" iconName=\"Linkedin-brand\" iconPosition=\"left\" [disabled]=\"true\" label=\"Add to profile\"></ui-button></div>\n </div>","props":{}},{"name":"LongLabelMultilined","template":"<div class=\"row\">\n <div class=\"colum\">\n <ui-button\n applicationTheme=\"${appTheme}\"\n [buttonBadgeConfig]='{\"badgeLabel\": \"Primary\"}'\n variant=\"primary\"\n ${argsToTemplate(args)}\n >\n\n </ui-button>\n </div>\n </div>","props":{"label":"'Some very long label that should be multilined for buttons that has long width'"}}],"documentation":"The primary interactive control for triggering actions. Follows a strict hierarchy: one primary action per screen, supported by secondary, ghost, and outlined variants for lower-priority actions. Destructive and icon-only variants cover delete flows and compact action bars. Used everywhere in the product — from assessment creation CTAs to table row actions.\n\n## When to use\n\n- **Primary for the single most important action** on a screen or section — one per view\n- **Secondary for the second most important action** when it needs equal visual weight to Primary\n- **A Tooltip on every Icon Button** — an icon with no label is inaccessible without one\n\n## When NOT to use\n\n- **More than one Primary button per screen section** → if you think you need two, one of them is wrong\n- **Ghost or Text buttons as primary actions** → their low visual weight signals low priority\n- **Disabling a button without explaining why** → use a tooltip or adjacent message, as disabled states without context frustrate users\n- **Icon-only actions on other variants** → icon-only buttons use `variant=\"icon-button\"` (round, borderless); `justIcon` on primary/secondary/ghost/text renders treatments design has rejected\n- **Displaying a state** → never use a (disabled) button as an 'already added / selected / completed' marker; show state on the container (e.g. selectable card selected state), a check icon, or a Badge. Disabled is only for real actions currently unavailable\n\n## How to use\n\nTo use the Button component, you need to import the `ButtonComponentModule` in your module:\n\n```typescript\nimport { ButtonComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ButtonComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-button label=\"Button Label\" applicationTheme=\"light\"></ui-button>\n```\n","whenToUse":["**Primary for the single most important action** on a screen or section — one per view","**Secondary for the second most important action** when it needs equal visual weight to Primary","**A Tooltip on every Icon Button** — an icon with no label is inaccessible without one"],"whenNotToUse":["**More than one Primary button per screen section** → if you think you need two, one of them is wrong","**Ghost or Text buttons as primary actions** → their low visual weight signals low priority","**Disabling a button without explaining why** → use a tooltip or adjacent message, as disabled states without context frustrate users","**Icon-only actions on other variants** → icon-only buttons use `variant=\"icon-button\"` (round, borderless); `justIcon` on primary/secondary/ghost/text renders treatments design has rejected","**Displaying a state** → never use a (disabled) button as an 'already added / selected / completed' marker; show state on the container (e.g. selectable card selected state), a check icon, or a Badge. Disabled is only for real actions currently unavailable"],"template":"<div\n #tooltipElement\n class=\"tooltip\"\n [matTooltip]=\"tooltip ? tooltip : labelEllipsis ? label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n></div>\n<div\n class=\"button-wrapper\"\n [ngClass]=\"{\n 'full-width': fullWidth,\n 'rounded-icon': applicationTheme !== 'classic' && icon && !label,\n link: !isButtonLink,\n }\"\n>\n <div\n class=\"button-container\"\n [ngClass]=\"{\n disabled: disabled,\n 'button-scale': buttonState === 'pressed' && applicationTheme !== 'classic' && !disabledScaleOnClick,\n }\"\n >\n @if (isPremium && typeIncluded) {\n <span class=\"right-top\">\n <img [attr.src]=\"'/images/premium.svg'\" [alt]=\"'premium-icon'\" />\n </span>\n }\n @if (buttonBadgeConfig?.badgeType) {\n <ui-badge\n id=\"badge\"\n [rebrandColor]=\"buttonBadgeConfig?.rebrandColor ?? 'brand'\"\n [variant]=\"buttonBadgeConfig!.badgeType!\"\n [label]=\"buttonBadgeConfig?.badgeLabel ?? ''\"\n [notificationsAmount]=\"buttonBadgeConfig?.badgeNotificationAmount ?? 0\"\n class=\"right-top\"\n [ngClass]=\"[\n buttonBadgeConfig?.badgeType === 'primary' ? 'badge-primary' : '',\n buttonBadgeConfig?.badgeType === 'notification' ? 'badge-notification' : '',\n variant,\n ]\"\n ></ui-badge>\n }\n @if (isButtonLink) {\n <button\n [style]=\"'--icon-button-size:' + iconButtonSize + 'px'\"\n [ngClass]=\"classCss\"\n [ngStyle]=\"styleCss\"\n [disabled]=\"disabled\"\n (click)=\"buttonClick($event)\"\n (mouseleave)=\"buttonHover($event)\"\n (mouseenter)=\"buttonHover($event)\"\n (mousedown)=\"onPressed($event)\"\n (mouseup)=\"onPressed($event)\"\n (keydown)=\"onKeydown($event)\"\n (keyup)=\"onKeyup($event)\"\n [attr.aria-required]=\"ariaRequired\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledbyId\"\n [attr.aria-describedby]=\"'describedby'\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"ariaExpanded\"\n [attr.aria-controls]=\"ariaControls\"\n [attr.aria-pressed]=\"ariaPressed\"\n [type]=\"type\"\n [disableRipple]=\"applicationTheme !== 'classic'\"\n [matTooltip]=\"tooltip ? tooltip : labelEllipsis ? label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n [matTooltipPosition]=\"tooltipPosition\"\n #buttonElement\n [attr.role]=\"role\"\n mat-flat-button\n >\n <span id=\"describedby\" [style.display]=\"'none'\">\n {{ isPremium ? label + ' ' + ('BUTTON.PREMIUM_FEATURE' | uiTranslate | async) : ariaDescribedby }}\n </span>\n <span [id]=\"ariaLabelledbyId\" [style.display]=\"'none'\">\n {{ ariaLabelledby }}\n </span>\n @if (showSpinner) {\n <span class=\"spinner-wrapper\">\n <mat-spinner class=\"position-spinner\" aria-label=\"loading\" mode=\"indeterminate\" diameter=\"24\"></mat-spinner>\n </span>\n } @else {\n @if (justIcon && !!icon) {\n <span class=\"icon-only-wrapper\">\n <span class=\"icon only\" role=\"label\">\n <ui-icon\n [color]=\"variant === 'ghost-ai' ? 'ai' : 'inherit'\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"icon\"\n [filled]=\"isFilledIcon || iconFilled\"\n [useFullIconName]=\"variant === 'primary' || variant === 'secondary' || variant === 'linkedin'\"\n [size]=\"size === 'small' ? '16' : '24'\"\n ></ui-icon>\n </span>\n </span>\n }\n @if (!justIcon) {\n <span class=\"icon-label-wrapper\" [ngClass]=\"{ 'center-text': iconName === '' }\">\n @if (showInlineSpinner) {\n <span class=\"icon inline-spinner\" aria-hidden=\"true\">\n <mat-spinner\n aria-label=\"loading\"\n mode=\"indeterminate\"\n [diameter]=\"size === 'small' ? 16 : 20\"\n ></mat-spinner>\n </span>\n }\n @if (iconPosition === 'left' && !!iconName && !showInlineSpinner) {\n <span class=\"icon\" aria-hidden=\"true\">\n <ui-icon\n [color]=\"variant === 'ghost-ai' ? 'ai' : 'inherit'\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"icon\"\n [filled]=\"isFilledIcon || iconFilled\"\n [useFullIconName]=\"variant === 'primary' || variant === 'secondary' || variant === 'linkedin'\"\n [size]=\"size === 'small' ? '16' : '24'\"\n ></ui-icon>\n </span>\n }\n @if (isLabel) {\n <span\n class=\"label\"\n id=\"label\"\n uiEllipseText\n [isMultiline]=\"true\"\n (onChangeTextState)=\"onLabelEllipsisChange($event)\"\n >\n {{ label }}\n </span>\n }\n @if (iconPosition === 'right' && !!icon) {\n <span class=\"icon\" aria-hidden=\"true\"\n ><ui-icon\n [color]=\"variant === 'ghost-ai' ? 'ai' : 'inherit'\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"icon\"\n [filled]=\"isFilledIcon || iconFilled\"\n [size]=\"size === 'small' ? '16' : '24'\"\n [useFullIconName]=\"variant === 'primary' || variant === 'secondary' || variant === 'linkedin'\"\n ></ui-icon\n ></span>\n }\n </span>\n }\n }\n </button>\n }\n @if (!isButtonLink) {\n <a\n #buttonElement\n [ngClass]=\"classCss\"\n [ngStyle]=\"styleCss\"\n [disabled]=\"disabled\"\n (click)=\"buttonClick($event)\"\n (mouseenter)=\"buttonHover($event)\"\n (mouseleave)=\"buttonHover($event)\"\n (mousedown)=\"onPressed($event)\"\n (mouseup)=\"onPressed($event)\"\n (keydown)=\"onKeydown($event)\"\n (keyup)=\"onKeyup($event)\"\n [disableRipple]=\"true\"\n [attr.aria-required]=\"ariaRequired\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledbyId\"\n [attr.aria-describedby]=\"'describedby-link'\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"ariaExpanded\"\n [attr.aria-controls]=\"ariaControls\"\n [attr.aria-pressed]=\"ariaPressed\"\n [matTooltip]=\"tooltip ? tooltip : labelEllipsis ? label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n role=\"link\"\n [href]=\"url\"\n [target]=\"urlTarget\"\n mat-flat-button\n >\n <span id=\"describedby-link\" [style.display]=\"'none'\">\n {{ ariaDescribedby }}\n </span>\n <span [id]=\"ariaLabelledbyId\" [style.display]=\"'none'\">\n {{ ariaLabelledby }}\n </span>\n <span class=\"icon-label-wrapper\">\n {{ label }}\n </span>\n </a>\n }\n </div>\n</div>\n","queryGuide":{"roles":["label","link","progressbar"],"ariaAttributes":["aria-label","aria-hidden","aria-required","aria-labelledby","aria-describedby","aria-disabled","aria-expanded","aria-controls","aria-pressed"],"dataTestIds":[],"materialComponents":["mat-spinner"],"childUIComponents":["ui-badge","ui-icon"],"suggestedQueries":["screen.getByRole('label')","screen.getByRole('link')","screen.getByRole('progressbar')","screen.getByLabelText('label text')"]},"filePath":"components/button/button.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=22807-2000","lastModified":1786626756380.5837},"card":{"name":"CardComponent","selector":"ui-card","category":"card","description":"A contained surface for grouping related content. Creates visual scannability in list and grid layouts. The State variant supports click/selection behaviour for interactive cards (e.g. test selection in assessment setup). Semantic variants — Educative, Premium, Error — communicate status without requiring additional badges.","importPath":"@testgorilla/tgo-ui/components/card","moduleName":"CardComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"CardSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"Card size. Defaults \"medium\".","isSignal":false},{"name":"sizeVariant","type":"CardSizeVariant","defaultValue":"'regular'","required":false,"alias":null,"transform":null,"description":"Card size variant. Defaults \"default\".","isSignal":false},{"name":"variant","type":"CardVariant","defaultValue":"'default'","required":false,"alias":null,"transform":null,"description":"Card variants. Defaults \"default\".","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"errorsSize","type":"unknown","defaultValue":"'default'","required":false,"alias":null,"transform":null,"description":"Errors size","isSignal":false},{"name":"selected","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Marks card as selected","isSignal":false},{"name":"allowSelect","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Marks card as readonly. Defaults \"false\".","isSignal":false},{"name":"allowFocus","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Marks card as focusable. Defaults \"true\".","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Show errors below card.","isSignal":false}],"outputs":[{"name":"cardSelected","eventType":"boolean","alias":null,"description":"Emits when card is being selected","isSignal":false}],"module":{"name":"CardComponentModule","imports":["CommonModule","IconComponentModule","CardComponent"],"exports":["CardComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"CardSize","value":"'small' | 'medium' | 'large'"},{"name":"CardSizeVariant","value":"'regular' | 'condensed'"},{"name":"CardVariant","value":"| 'default'\n | 'shadow'\n | 'state'\n | 'error'\n | 'educative'\n | 'premium'\n | 'ai'\n | 'actionDriver'\n | 'neutral'\n | 'neutral-state'"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A contained surface for grouping related content. Creates visual scannability in list and grid layouts. The State variant supports click/selection behaviour for interactive cards (e.g. test selection in assessment setup). Semantic variants — Educative, Premium, Error — communicate status without requiring additional badges.\n\n## When to use\n\n- **State cards** when the entire card is the interaction target — selecting a test, choosing a plan\n- **The correct semantic variant** to communicate status inline — Error for validation failures, Educative for guidance, Premium for upgrade prompts\n- **The size prop** (small/medium/large) to pad card content rather than overriding padding manually\n\n## When NOT to use\n\n- **Nested interactive elements inside a State card** → don't add them without clearly separating their tap targets\n- **The Default card as a button** → use the State variant with proper selection handling instead\n- **Mixed variants in the same list** → don't stack them, it creates visual inconsistency and confuses hierarchy\n\n## How to use\n\nTo use the Card component, you need to import the `CardComponentModule` in your module:\n\n```typescript\nimport { CardComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n CardComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-card applicationTheme=\"light\" variant=\"default\">\n <div>Content goes here</div>\n</ui-card>\n```\n","whenToUse":["**State cards** when the entire card is the interaction target — selecting a test, choosing a plan","**The correct semantic variant** to communicate status inline — Error for validation failures, Educative for guidance, Premium for upgrade prompts","**The size prop** (small/medium/large) to pad card content rather than overriding padding manually"],"whenNotToUse":["**Nested interactive elements inside a State card** → don't add them without clearly separating their tap targets","**The Default card as a button** → use the State variant with proper selection handling instead","**Mixed variants in the same list** → don't stack them, it creates visual inconsistency and confuses hierarchy"],"template":"<div\n class=\"card-container\"\n (keydown.enter)=\"selectCard()\"\n (keydown.space)=\"selectCard()\"\n (click)=\"selectCard()\"\n [ngClass]=\"[classes, isSelected ? 'selected' : '']\"\n [attr.theme]=\"applicationTheme\"\n [attr.tabindex]=\"allowFocus ? 0 : null\"\n>\n <ng-content></ng-content>\n</div>\n@if (safeErrors?.length) {\n <div class=\"errors\" [class.errors-small]=\"errorsSize === 'small'\">\n @for (error of safeErrors; track error) {\n <div class=\"error\">\n <ui-icon [size]=\"errorIconSize\" [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span [innerHTML]=\"error\"></span>\n </div>\n }\n </div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":[]},"filePath":"components/card/card.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=369-19097","lastModified":1786626756381.0803},"checkbox":{"name":"CheckboxComponent","selector":"ui-checkbox","category":"forms","description":"A binary input for yes/no choices and multi-select lists. Supports checked, unchecked, partial (indeterminate), and disabled states. Used in bulk selection (select all in candidate table), setting toggles within forms where immediate effect isn't implied, and multi-option question types in custom assessments.","importPath":"@testgorilla/tgo-ui/components/checkbox","moduleName":"CheckboxComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the checkbox is disabled.\nDefault: false.","isSignal":false},{"name":"checked","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the checkbox is checked.\nDefault: false.","isSignal":false},{"name":"indeterminate","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the checkbox is indeterminate.\nIt can be used to represent a checkbox with three states, e.g. a checkbox that represents a nested list of checkable items.\nDefault: false.","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines whether the checkbox color.\nDefault: Test Gorilla primary color.","isSignal":false},{"name":"name","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Name value will be applied to the input element if present.","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Text content will be applied to the input element if present.","isSignal":false},{"name":"multiple","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Renders the checkbox as a bordered, padded multiple-choice answer cell\n(the candidate test-taking experience). NOT for multi-select or table\nrow selection — use the plain default checkbox there.\nDefault: false","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"hasError","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Defines if checkbox has error","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in-build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"ariaLabelledby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"truncateText","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Truncate text if it is too long","isSignal":false},{"name":"alignment","type":"Alignment","defaultValue":"'center'","required":false,"alias":null,"transform":null,"description":"Text alignment","isSignal":false},{"name":"tabIndex","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Sets the tab index for the checkbox component.\nThis determines the order in which elements receive focus when the user navigates through them using the Tab key.\nDefault value is 0.","isSignal":false}],"outputs":[{"name":"changed","eventType":"boolean","alias":null,"description":"Event emitted when the checkbox's checked value changes.","isSignal":false}],"module":{"name":"CheckboxComponentModule","imports":["CommonModule","MatCheckboxModule","FormsModule","ReactiveFormsModule","MatInputModule","IconComponentModule","UiTranslatePipe","ValidationErrorModule","HasValidationErrorPipe","EllipseTextDirective","MatTooltipModule","FocusVisibleDirective","CheckboxComponent"],"exports":["CheckboxComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"MultipleChoice","template":"<ui-checkbox applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - unselected\" [checked]=\"false\"></ui-checkbox>\n <br>\n <ui-checkbox applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - selected\" [checked]=\"true\"></ui-checkbox>","props":{}},{"name":"WithErrorMultiple","template":"<ui-checkbox [hasError]=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - unselected\" [checked]=\"false\"></ui-checkbox>\n <ui-checkbox [hasError]=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - unselected\" [checked]=\"false\"></ui-checkbox>","props":{}},{"name":"WithErrorSingle","template":"<ui-checkbox [hasError]=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"false\" label=\"Single selection - unselected\" [checked]=\"false\"></ui-checkbox>\n <ui-checkbox [hasError]=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"false\" label=\"Single selection - unselected\" [checked]=\"false\"></ui-checkbox>","props":{}}],"documentation":"A binary input for yes/no choices and multi-select lists. Supports checked, unchecked, partial (indeterminate), and disabled states. Used in bulk selection (select all in candidate table), setting toggles within forms where immediate effect isn't implied, and multi-option question types in custom assessments.\n\n## When to use\n\n- **Multi-select scenarios** — when users can choose more than one option from a list\n- **'Select all' headers** — use the Partial state when only some items in a group are checked\n- **A visible label** — always pair the Checkbox with associated text, never use it on its own\n- **The `multiple` variant for candidate-facing multiple-choice questions** — it renders the bordered, padded answer cell used in the test-taking experience\n\n## When NOT to use\n\n- **A single binary setting that takes immediate effect** → use Toggle instead\n- **Mutually exclusive single-choice selections** → use Radio Button\n- **Label styled as a heading or button** → it should clearly read as a form control\n- **The `multiple` variant in tables or filter lists** → it's the test-taking answer cell; row selection uses the default checkbox\n\n## How to use\n\nTo use the Checkbox component, you need to import the `CheckboxComponentModule` in your module:\n\n```typescript\nimport { CheckboxComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n CheckboxComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-checkbox\n label=\"Accept terms and conditions\"\n [checked]=\"isAccepted\"\n [disabled]=\"isDisabled\"\n [multiple]=\"false\"\n applicationTheme=\"light\"\n (checkedChange)=\"onCheckboxChange($event)\"\n>\n</ui-checkbox>\n```\n\nYou can also use it with reactive forms:\n\n```html\n<ui-checkbox formControlName=\"acceptTerms\" label=\"Accept terms and conditions\"> </ui-checkbox>\n```\n\nThe checkbox component supports standard, multiple choice, and indeterminate states.\n","whenToUse":["**Multi-select scenarios** — when users can choose more than one option from a list","**'Select all' headers** — use the Partial state when only some items in a group are checked","**A visible label** — always pair the Checkbox with associated text, never use it on its own","**The `multiple` variant for candidate-facing multiple-choice questions** — it renders the bordered, padded answer cell used in the test-taking experience"],"whenNotToUse":["**A single binary setting that takes immediate effect** → use Toggle instead","**Mutually exclusive single-choice selections** → use Radio Button","**Label styled as a heading or button** → it should clearly read as a form control","**The `multiple` variant in tables or filter lists** → it's the test-taking answer cell; row selection uses the default checkbox"],"template":"<div\n #checkboxContainer\n tabindex=\"0\"\n [class]=\"multiple ? 'checkbox-container' : 'checkbox'\"\n [ngClass]=\"classMultiple\"\n [attr.alignment]=\"alignment\"\n [attr.truncate-text]=\"truncateText\"\n [attr.theme]=\"applicationTheme\"\n [attr.hasError]=\"hasError\"\n (keydown.enter)=\"toggleChecked(); checkboxContainer.focus()\"\n (keydown.space)=\"toggleChecked(); checkboxContainer.focus()\"\n>\n <mat-checkbox\n #checkbox\n (mouseleave)=\"checkbox.disableRipple = true\"\n (mouseenter)=\"checkbox.disableRipple = false\"\n [indeterminate]=\"indeterminate\"\n [checked]=\"checked\"\n [disabled]=\"disabled\"\n [tabIndex]=\"tabIndex\"\n [name]=\"name\"\n [ngClass]=\"[checkbox.disableRipple ? 'disable-ripple' : '', origin ? 'cdk-' + origin + '-focused' : '']\"\n [aria-label]=\"ariaLabel\"\n [aria-labelledby]=\"ariaLabelledby ? ariaLabelledby : label\"\n [aria-describedby]=\"ariaDescribedby || 'ariaDescribedby'\"\n (click)=\"toggleChecked()\"\n (keydown.enter)=\"$event.preventDefault()\"\n (keydown.space)=\"$event.preventDefault()\"\n class=\"checkbox\"\n >\n @if (label) {\n <span\n #tooltip=\"matTooltip\"\n class=\"label\"\n uiEllipseText\n (onChangeTextState)=\"onLabelEllipsisChange($event)\"\n [matTooltip]=\"showTooltip && truncateText ? label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ label }}\n </span>\n }\n <ng-content select=\"[checkbox-label]\"></ng-content>\n </mat-checkbox>\n\n @if (ngControl?.errors | hasValidationError) {\n <mat-hint class=\"error\">\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error [ngControl]=\"ngControl\" [label]=\"hideLabelInErrors ? null : label\"></ui-validation-error>\n }\n </mat-hint>\n }\n</div>\n","queryGuide":{"roles":["checkbox"],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-checkbox","mat-hint"],"childUIComponents":["ui-validation-error"],"suggestedQueries":["screen.getByRole('checkbox')"]},"filePath":"components/checkbox/checkbox.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=3132-19050","lastModified":1786626756381.6804},"checklist":{"name":"ChecklistComponent","selector":"ui-checklist","category":"checklist","description":"A structured list of items with check or status indicators. Used in onboarding flows, assessment setup checklists, and pre-submission review steps to show users what's complete, pending, or missing before they proceed.","importPath":"@testgorilla/tgo-ui/components/checklist","moduleName":"ChecklistComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Label for the checklist","isSignal":true},{"name":"description","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Sub title/Description for the checklist","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"Application theme","isSignal":true},{"name":"footerButtonLabel","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"Confirm button label","isSignal":true},{"name":"ariaLabel","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":true},{"name":"language","type":"unknown","defaultValue":"LanguageService.defaultLanguage","required":false,"alias":null,"transform":null,"description":"The language to be used","isSignal":true},{"name":"autocheck","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"A boolean property that determines whether the checklist items should be automatically checked.","isSignal":true},{"name":"items","type":"ChecklistItem[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Items for the checklist","isSignal":true},{"name":"selectedItem","type":"ChecklistItem","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Represents the currently selected item in the checklist.\nThis is an instance of the `ChecklistItem` supports two-way binding.","isSignal":true}],"outputs":[{"name":"footerButtonClicked","eventType":"void","alias":null,"description":"Event emitter that triggers when the footer button is clicked.\nEmits a void event.","isSignal":true},{"name":"completed","eventType":"void","alias":null,"description":"An output event emitter that signifies the completion of a task or process.\nThis event does not carry any payload.","isSignal":true},{"name":"itemsChange","eventType":"ChecklistItem[]","alias":null,"description":"Two-way binding change event for items","isSignal":true},{"name":"selectedItemChange","eventType":"ChecklistItem","alias":null,"description":"Two-way binding change event for selectedItem","isSignal":true}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"ChecklistItem","properties":[{"name":"label","type":"string","optional":false},{"name":"value","type":"string","optional":false},{"name":"checked","type":"boolean","optional":false},{"name":"description","type":"string","optional":true},{"name":"hasLink","type":"boolean","optional":true},{"name":"disabled","type":"boolean","optional":true},{"name":"reselect","type":"boolean","optional":true}]}],"enums":[]},"examples":[],"documentation":"A structured list of items with check or status indicators. Used in onboarding flows, assessment setup checklists, and pre-submission review steps to show users what's complete, pending, or missing before they proceed.\n\n## When to use\n\n- **Verifying a sequence** of conditions or steps before the user takes an action\n- **Pair with a primary Button** (e.g. 'Continue') that activates only when required items are checked\n- **Short, action-oriented labels** — 'Add at least one test', not 'Tests have been added'\n\n## When NOT to use\n\n- **Data lists or navigation menus** → it's for task verification, not content display\n- **More than 7–8 items** → if setup is that complex, break it into steps with a Stepper\n- **Optional items as blockers** → if an item is optional, make that explicit in the label\n\n## How to use\n\nTo use the Checklist component, you need to import the component in your module:\n\n```typescript\nimport { ChecklistComponent } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ChecklistComponent,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-checklist\n [items]=\"items\"\n label=\"My Checklist\"\n [applicationTheme]=\"theme\"\n footerButtonLabel=\"Continue\"\n (footerButtonClicked)=\"onButtonClick()\"\n (completed)=\"onCompleted()\"\n>\n</ui-checklist>\n```\n","whenToUse":["**Verifying a sequence** of conditions or steps before the user takes an action","**Pair with a primary Button** (e.g. 'Continue') that activates only when required items are checked","**Short, action-oriented labels** — 'Add at least one test', not 'Tests have been added'"],"whenNotToUse":["**Data lists or navigation menus** → it's for task verification, not content display","**More than 7–8 items** → if setup is that complex, break it into steps with a Stepper","**Optional items as blockers** → if an item is optional, make that explicit in the label"],"template":"@if (label()) {\n <h4 [attr.aria-label]=\"ariaLabel() ?? label()\">{{ label() }}</h4>\n}\n@if (description()) {\n <div aria-label=\"description\" class=\"description\" [innerHTML]=\"description()\"></div>\n}\n\n<ui-progress-bar [showPercentage]=\"true\" [progress]=\"progress()\"></ui-progress-bar>\n\n<div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ 'COMMON.PROGRESS' | uiTranslate: { progress: progress() } | async }}\n</div>\n\n<ui-divider size=\"small\" companyColor=\"#d3d3d3\"> </ui-divider>\n\n<div class=\"checklist-container\" data-testid=\"checklist-container\">\n @for (item of items(); track item.value + item.checked; let i = $index) {\n <div\n data-testid=\"checklist-item\"\n class=\"checklist-item\"\n [class.disabled]=\"item.disabled\"\n [class.checked]=\"item.checked\"\n [class.active]=\"item === selectedItem() && !autocheck()\"\n [attr.aria-labelledby]=\"'item-' + item.value\"\n [tabindex]=\"item?.disabled ? -1 : 0\"\n (click)=\"selectItem(item)\"\n (keydown)=\"keydownHandler($event, item)\"\n >\n <div class=\"content\">\n <ui-icon\n [name]=\"item.checked ? 'Check-in-line' : 'Circle-in-line'\"\n [filled]=\"item.checked\"\n class=\"checked\"\n [applicationTheme]=\"theme()\"\n [size]=\"'16'\"\n [color]=\"item.checked ? 'white' : item.disabled ? '#919191' : 'dark'\"\n ></ui-icon>\n <div class=\"label-and-description\">\n <span [id]=\"'item-' + item.value\" class=\"label\">{{ item.label }}</span>\n @if (item.description) {\n <span class=\"item-description\">{{ item.description }}</span>\n }\n </div>\n </div>\n\n @if (item.hasLink && !item.disabled) {\n <div class=\"navigation\">\n <ui-icon\n [tabindex]=\"-1\"\n class=\"link\"\n [name]=\"'Arrow-chevron-right-filled'\"\n [applicationTheme]=\"theme()\"\n [size]=\"'24'\"\n color=\"rebrand-black\"\n ></ui-icon>\n </div>\n }\n </div>\n }\n</div>\n@if (footerButtonLabel()) {\n <div class=\"action-btn\">\n <ui-button\n data-testid=\"checklist-footer-button\"\n [label]=\"footerButtonLabel()!\"\n [variant]=\"'link'\"\n [companyColor]=\"'#000'\"\n (buttonClickEvent)=\"footerButtonClick($event)\"\n ></ui-button>\n </div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label","aria-live","aria-labelledby"],"dataTestIds":["checklist-container","checklist-item","checklist-footer-button"],"materialComponents":[],"childUIComponents":["ui-progress-bar","ui-divider","ui-icon","ui-button"],"suggestedQueries":["screen.getByLabelText('label text')","screen.getByTestId('checklist-container')","screen.getByTestId('checklist-item')","screen.getByTestId('checklist-footer-button')"]},"filePath":"components/checklist/checklist.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=23683-4302","lastModified":1786626756382.6746},"datepicker":{"name":"DatepickerComponent","selector":"ui-datepicker","category":"forms","description":"A date selection input with a calendar popover. Supports single date and date range selection. Used in assessment deadline configuration, candidate invitation scheduling, and any form field that requires a specific calendar date.","importPath":"@testgorilla/tgo-ui/components/datepicker","moduleName":"DatepickerComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Form field label","isSignal":false},{"name":"fieldName","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input name attribute","isSignal":false},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input placeholder","isSignal":false},{"name":"id","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input id","isSignal":false},{"name":"value","type":"Array<Date | null> | Date","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input value","isSignal":false},{"name":"isRange","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if input is date range","isSignal":false},{"name":"setSingleDateRangeOnBlur","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if on blur of calendar and no end date is selected, set the end date as the start date","isSignal":false},{"name":"showInnerErrors","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if inner errors should be shown","isSignal":false},{"name":"innerErrorsMessage","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines inner errors message","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if input is disabled","isSignal":false},{"name":"required","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input is required or not","isSignal":false},{"name":"hintMessage","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Hint text","isSignal":false},{"name":"updateOnBlur","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input is update only on blur","isSignal":false},{"name":"showBottomContent","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show reserved content below form field","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Set full width DatepickerComponent","isSignal":false},{"name":"minDate","type":"Date | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Set min date for Datepicker","isSignal":false},{"name":"maxDate","type":"Date | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Set max date for Datepicker","isSignal":false},{"name":"monthPicker","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show and pick date without day","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in-build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"preventUserInput","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Prevent user input","isSignal":false},{"name":"companyColor","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the option hover.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input field errors","isSignal":false},{"name":"browserTimezone","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Use browser timezone","isSignal":false}],"outputs":[{"name":"changed","eventType":"Date | Date[]","alias":null,"description":"Event emitted when the value is change - when used outside of form","isSignal":false}],"module":{"name":"DatepickerComponentModule","imports":["CommonModule","MatFormFieldModule","MatInputModule","IconComponentModule","FormsModule","ReactiveFormsModule","MatDatepickerModule","MatNativeDateModule","UiTranslatePipe","NoDateFormatDirective","HasValidationErrorPipe","ValidationErrorModule","DatepickerComponent"],"exports":["DatepickerComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"WithPlaceholder","template":"<ui-datepicker\n data-testid=\"datepicker\"\n ${argsToTemplate(args)}\n applicationTheme=\"${appTheme}\"\n (changed)=\"changed($event)\"\n ></ui-datepicker>","props":{"label":"'Date'","placeholder":"'DD/MM/YYYY'"}},{"name":"WithDisable","template":"<ui-datepicker \n ${argsToTemplate(args)}\n applicationTheme=\"${appTheme}\"\n (changed)=\"changed($event)\"\n ></ui-datepicker>","props":{"disabled":"true"}}],"documentation":"A date selection input with a calendar popover. Supports single date and date range selection. Used in assessment deadline configuration, candidate invitation scheduling, and any form field that requires a specific calendar date.\n\n## When to use\n\n- **Calendar dates** — use the Datepicker instead of a plain text input that asks users to type a date manually\n- **Sensible defaults** — default to the most likely date range for the context (e.g. today + 30 days for assessment deadlines)\n- **Human-readable display** — always show the selected date in a readable format in the input field\n\n## When NOT to use\n\n- **Time-of-day selection** → pair the Datepicker with a separate time input if both are needed\n- **Relative date selections** ('In 7 days', 'Next month') → use a Dropdown or Segmented Button instead\n- **Dates beyond meaningful limits** → don't let users enter them without validation\n\n## How to use\n\nTo use the Datepicker component, you need to import the `DatepickerComponentModule` in your module:\n\n```typescript\nimport { DatepickerComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n DatepickerComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-datepicker\n label=\"Select Date\"\n [value]=\"selectedDate\"\n [isRange]=\"false\"\n [required]=\"true\"\n applicationTheme=\"light\"\n (changed)=\"onDateChanged($event)\"\n>\n</ui-datepicker>\n```\n\nFor date ranges:\n\n```html\n<ui-datepicker label=\"Date Range\" [isRange]=\"true\" applicationTheme=\"light\" (changed)=\"onRangeChanged($event)\">\n</ui-datepicker>\n```\n\nThe datepicker component supports both single date selection and date ranges.\n","whenToUse":["**Calendar dates** — use the Datepicker instead of a plain text input that asks users to type a date manually","**Sensible defaults** — default to the most likely date range for the context (e.g. today + 30 days for assessment deadlines)","**Human-readable display** — always show the selected date in a readable format in the input field"],"whenNotToUse":["**Time-of-day selection** → pair the Datepicker with a separate time input if both are needed","**Relative date selections** ('In 7 days', 'Next month') → use a Dropdown or Segmented Button instead","**Dates beyond meaningful limits** → don't let users enter them without validation"],"template":"@if (monthPicker) {\n <mat-form-field\n appNoDateFormat\n appearance=\"outline\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) ? 'warn' : 'accent'\"\n [ngClass]=\"{ 'hide-bottom-content': !showBottomContent, 'full-width': fullWidth }\"\n >\n @if (label) {\n <mat-label>\n <ng-container [ngTemplateOutlet]=\"labelTpl\"></ng-container>\n </mat-label>\n }\n\n <input\n data-testid=\"datepicker-input\"\n matInput\n [id]=\"id!\"\n (blur)=\"onTouch()\"\n [placeholder]=\"placeholder!\"\n [matDatepicker]=\"dp\"\n [value]=\"value\"\n [disabled]=\"disabled\"\n [name]=\"fieldName!\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n [readonly]=\"preventUserInput\"\n (change)=\"onInput($event)\"\n (dateChange)=\"onValueChange($event.value)\"\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\">\n <ui-icon\n matDatepickerToggleIcon\n [applicationTheme]=\"applicationTheme\"\n name=\"Calendar\"\n size=\"24\"\n color=\"inherit\"\n ></ui-icon>\n </mat-datepicker-toggle>\n <mat-datepicker\n panelClass=\"month-picker\"\n #dp\n startView=\"multi-year\"\n (monthSelected)=\"setMonthAndYear($event, dp)\"\n ></mat-datepicker>\n\n @if (hint && !_errors && !(ngControl?.touched && (ngControl?.errors | hasValidationError))) {\n <mat-hint class=\"info\">{{ hint }} </mat-hint>\n }\n @if (errorsLength || (ngControl?.touched && (ngControl?.errors | hasValidationError))) {\n <mat-hint class=\"error\">\n <ng-container [ngTemplateOutlet]=\"hintsTpl\"></ng-container>\n </mat-hint>\n }\n </mat-form-field>\n} @else {\n @if (isRange) {\n <mat-form-field\n appearance=\"outline\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) ? 'warn' : 'accent'\"\n [ngClass]=\"{ 'hide-bottom-content': !showBottomContent, 'full-width': fullWidth }\"\n class=\"range-datepicker\"\n >\n @if (label) {\n <mat-label>\n <ng-container [ngTemplateOutlet]=\"labelTpl\"></ng-container>\n </mat-label>\n }\n\n <mat-date-range-input [formGroup]=\"range\" [rangePicker]=\"rangePicker\" [min]=\"minDate\" [max]=\"maxDate\">\n <input\n data-testid=\"datepicker-range-start\"\n matStartDate\n formControlName=\"start\"\n [placeholder]=\"translationContext + 'START_DATE' | uiTranslate | async\"\n (dateChange)=\"dateRangeChange()\"\n (blur)=\"setSingleDateRangeOnBlur && fillSingleDateRange(true)\"\n />\n <input\n data-testid=\"datepicker-range-end\"\n matEndDate\n formControlName=\"end\"\n [placeholder]=\"translationContext + 'END_DATE' | uiTranslate | async\"\n (dateChange)=\"dateRangeChange()\"\n (blur)=\"setSingleDateRangeOnBlur && fillSingleDateRange(false)\"\n />\n </mat-date-range-input>\n <mat-datepicker-toggle matIconSuffix [for]=\"rangePicker\">\n <ui-icon\n matDatepickerToggleIcon\n [applicationTheme]=\"applicationTheme\"\n name=\"Calendar\"\n size=\"24\"\n color=\"inherit\"\n ></ui-icon>\n </mat-datepicker-toggle>\n <mat-date-range-picker #rangePicker (closed)=\"handleCloseRangePicker()\"></mat-date-range-picker>\n\n @if (\n hint &&\n !_errors &&\n !(ngControl?.touched && (ngControl?.errors | hasValidationError)) &&\n !(range?.invalid && showInnerErrors)\n ) {\n <mat-hint class=\"info\">{{ hint }} </mat-hint>\n }\n @if (\n errorsLength ||\n (ngControl?.touched && (ngControl?.errors | hasValidationError)) ||\n (range?.invalid && showInnerErrors)\n ) {\n <mat-hint class=\"error\">\n <ng-container [ngTemplateOutlet]=\"hintsTpl\"></ng-container>\n </mat-hint>\n }\n </mat-form-field>\n } @else {\n <mat-form-field\n appearance=\"outline\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) ? 'warn' : 'accent'\"\n [ngClass]=\"{ 'hide-bottom-content': !showBottomContent, 'full-width': fullWidth }\"\n >\n @if (label) {\n <mat-label>\n <ng-container [ngTemplateOutlet]=\"labelTpl\"></ng-container>\n </mat-label>\n }\n <input\n data-testid=\"datepicker-picker-input\"\n matInput\n [matDatepicker]=\"picker\"\n (blur)=\"onTouch()\"\n (dateChange)=\"onValueChange($event.value)\"\n [id]=\"id!\"\n [placeholder]=\"placeholder!\"\n [value]=\"value\"\n [disabled]=\"disabled\"\n [name]=\"fieldName!\"\n [readonly]=\"preventUserInput\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\">\n <ui-icon\n matDatepickerToggleIcon\n [applicationTheme]=\"applicationTheme\"\n name=\"Calendar\"\n size=\"24\"\n color=\"inherit\"\n ></ui-icon>\n </mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n\n @if (hint && !_errors && !(ngControl?.touched && (ngControl?.errors | hasValidationError))) {\n <mat-hint class=\"info\">{{ hint }} </mat-hint>\n }\n @if (errorsLength || (ngControl?.touched && (ngControl?.errors | hasValidationError))) {\n <mat-hint class=\"error\">\n <ng-container [ngTemplateOutlet]=\"hintsTpl\"></ng-container>\n </mat-hint>\n }\n </mat-form-field>\n }\n}\n\n<ng-template #labelTpl>{{ label + (required ? ' *' : '') }}</ng-template>\n<ng-template #hintsTpl>\n @if (errorsLength) {\n @for (error of _errors; track error) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" name=\"Error\"></ui-icon>\n <span [innerHTML]=\"error\"></span>\n </div>\n }\n }\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error [ngControl]=\"ngControl\" [label]=\"hideLabelInErrors ? null : label\"></ui-validation-error>\n }\n @if (showInnerErrors) {\n @if (isRange) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" name=\"Error\"></ui-icon>\n <span [innerHTML]=\"innerErrorsMessage\"></span>\n </div>\n }\n }\n</ng-template>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":["datepicker-input","datepicker-range-start","datepicker-range-end","datepicker-picker-input"],"materialComponents":["mat-form-field","mat-label","mat-datepicker-toggle","mat-datepicker","mat-hint","mat-date-range-input","mat-date-range-picker"],"childUIComponents":["ui-icon","ui-validation-error"],"suggestedQueries":["screen.getByTestId('datepicker-input')","screen.getByTestId('datepicker-range-start')","screen.getByTestId('datepicker-range-end')","screen.getByTestId('datepicker-picker-input')"]},"filePath":"components/datepicker/datepicker.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1684-69511","lastModified":1786626756392.0498},"dialog":{"name":"DialogComponent","selector":"ui-dialog","category":"dialog","description":"The base dialog component that powers Confirm Dialog, Custom Dialog, and Media Dialog. Provides the modal backdrop, focus trap, scroll-lock, and animation. Available in small and large sizes. Use the specific sub-variants for most cases; use the base Dialog only when building a new dialog pattern not covered by existing variants.","importPath":"@testgorilla/tgo-ui/components/dialog","moduleName":"DialogComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"title","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Dialog title","isSignal":false},{"name":"showCloseButton","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show close button","isSignal":false},{"name":"canCloseFn","type":"() => boolean","defaultValue":"() => true","required":false,"alias":null,"transform":null,"description":"Fuction called before dialgo is closed","isSignal":false},{"name":"secondaryButtonLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Secondary button label","isSignal":false},{"name":"footerMessage","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Footer dialog message","isSignal":false},{"name":"primaryButtonLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Primary button label","isSignal":false},{"name":"primaryButtonIconName","type":"IconName | ''","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Primary button icon name","isSignal":false},{"name":"secondaryButtonType","type":"ButtonColor","defaultValue":"'ghost'","required":false,"alias":null,"transform":null,"description":"Secondary button type","isSignal":false},{"name":"primaryButtonType","type":"ButtonColor","defaultValue":"'primary'","required":false,"alias":null,"transform":null,"description":"Primary button type","isSignal":false},{"name":"primaryButtonDataTestId","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Primary button test id","isSignal":false},{"name":"secondaryButtonDataTestId","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Secondary button test id","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the dialog.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"disablePrimaryButton","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Disable primary button","isSignal":false},{"name":"disableClose","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Disable close button","isSignal":false},{"name":"shouldDisableButtons","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Disable button when canCloseFn is used","isSignal":false},{"name":"ariaLabelledby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"keyboardOpen","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Defines how modal was opened (with keyboard or not)","isSignal":false},{"name":"primaryButtonIconPosition","type":"ButtonIconPosition","defaultValue":"'left'","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"closeEvent","eventType":"Event","alias":null,"description":"","isSignal":false},{"name":"secondaryButtonClickEvent","eventType":"Event","alias":null,"description":"","isSignal":false},{"name":"primaryButtonClickEvent","eventType":"Event","alias":null,"description":"","isSignal":false}],"module":{"name":"DialogComponentModule","imports":["CommonModule","MatDialogModule","ButtonComponentModule","UiTranslatePipe","DialogComponent"],"exports":["DialogComponent"],"declarations":[],"providers":["{ provide: MAT_DIALOG_DATA, useValue: {} }","{ provide: MatDialogRef, useValue: {} }","DialogService"]},"types":{"typeAliases":[],"interfaces":[{"name":"DialogConfig","properties":[{"name":"applicationTheme","type":"ApplicationTheme","optional":true},{"name":"size","type":"DialogSize","optional":true},{"name":"panelClass","type":"string","optional":true},{"name":"extraData","type":"T","optional":true},{"name":"backdropClass","type":"string","optional":true},{"name":"keyboardOpen","type":"boolean","optional":true},{"name":"config","type":"MatDialogConfig","optional":true}]}],"enums":[]},"examples":[{"name":"Default","template":"<ui-dialog ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n <div>This is the content of the dialog</div>\n </ui-dialog>","props":{"title":"'Modal title'","primaryButtonLabel":"'Confirm'","secondaryButtonLabel":"'Cancel'"}},{"name":"WithFooterContent","template":"<ui-dialog ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n <div>This is the content of the dialog</div>\n <ui-toggle dialogFooter label=\"Don't show this again\" applicationTheme=\"${appTheme}\"></ui-toggle>\n </ui-dialog>","props":{"title":"'Modal title'","primaryButtonLabel":"'Confirm'","secondaryButtonLabel":"'Cancel'"}},{"name":"WithPrimaryButtonIcon","template":"<ui-dialog ${argsToTemplate(args)} applicationTheme=\"${appTheme}\">\n <div>This is the content of the dialog</div>\n </ui-dialog>","props":{"title":"'Modal title'","primaryButtonLabel":"'Confirm'","secondaryButtonLabel":"'Cancel'","primaryButtonIconName":"'Info-in-line'","primaryButtonIconPosition":"'right'"}}],"documentation":"The base dialog component that powers Confirm Dialog, Custom Dialog, and Media Dialog. Provides the modal backdrop, focus trap, scroll-lock, and animation. Available in small and large sizes. Use the specific sub-variants for most cases; use the base Dialog only when building a new dialog pattern not covered by existing variants.\n\n## When to use\n\n- **Small size (default)** for focused tasks with limited content — confirmations, short forms\n- **Large size** for content-rich dialogs where more space is genuinely required — preview, detailed settings\n- **A clear dismissal path** — always provide a close button, escape key handler, or backdrop click\n\n## When NOT to use\n\n- **Base Dialog directly** → if Confirm Dialog or Custom Dialog already fits the need\n- **Non-blocking feedback** → use Snackbar or Alert Banner instead\n- **Inside scrollable content** → dialogs should always render at the viewport level\n\n## How to use\n\nTo use the Dialog component, you need to import the `DialogComponentModule` in your module:\n\n```typescript\nimport { DialogComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n DialogComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-dialog\n [title]=\"'Your Dialog Title'\"\n [primaryButtonLabel]=\"'Confirm'\"\n [secondaryButtonLabel]=\"'Cancel'\"\n [applicationTheme]=\"'light'\"\n (closeEvent)=\"onClose()\"\n (primaryButtonClickEvent)=\"onConfirm()\"\n (secondaryButtonClickEvent)=\"onCancel()\"\n>\n Your dialog content goes here\n</ui-dialog>\n```\n\n### Footer content\n\nProject arbitrary markup into the footer, to the left of the action buttons, using the `dialogFooter` attribute. This is useful for links, inline notices, or a \"don't show again\" checkbox that need real Angular markup rather than the plain-text `footerMessage` input. The action buttons remain aligned to the right.\n\nBecause it is real content projection, you can also render other library components in the footer — for example a `ui-toggle`:\n\n```html\n<ui-dialog [title]=\"'Your Dialog Title'\" [primaryButtonLabel]=\"'Confirm'\" [secondaryButtonLabel]=\"'Cancel'\">\n Your dialog content goes here\n\n <ui-toggle dialogFooter label=\"Don't show this again\"></ui-toggle>\n</ui-dialog>\n```\n","whenToUse":["**Small size (default)** for focused tasks with limited content — confirmations, short forms","**Large size** for content-rich dialogs where more space is genuinely required — preview, detailed settings","**A clear dismissal path** — always provide a close button, escape key handler, or backdrop click"],"whenNotToUse":["**Base Dialog directly** → if Confirm Dialog or Custom Dialog already fits the need","**Non-blocking feedback** → use Snackbar or Alert Banner instead","**Inside scrollable content** → dialogs should always render at the viewport level"],"template":"<div class=\"dialog-container\" [attr.theme]=\"applicationTheme\">\n <div\n mat-dialog-title\n class=\"dialog-title\"\n [attr.aria-modal]=\"true\"\n [attr.aria-labelledby]=\"'labelledby'\"\n [attr.aria-describedby]=\"'ariaDescribedby'\"\n >\n <span [style.display]=\"'none'\" id=\"ariaDescribedby\">{{ ariaDescribedby }}</span>\n\n <!-- Default header content -->\n @if (!hasCustomHeader) {\n <h3 class=\"bold\" id=\"labelledby\">{{ title }}</h3>\n @if (showCloseButton) {\n <div>\n <ui-button\n #closeButton\n [variant]=\"'icon-button'\"\n [iconName]=\"'Close'\"\n [companyColor]=\"applicationTheme === 'classic' ? '#000' : '#242424'\"\n [applicationTheme]=\"applicationTheme\"\n [tooltip]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n (click)=\"onClose($event)\"\n (buttonClickEvent)=\"dismiss()\"\n id=\"dialog-close-button\"\n ></ui-button>\n </div>\n }\n }\n\n <!-- Custom header content projection -->\n <ng-content select=\"[dialogHeader]\"></ng-content>\n </div>\n <mat-dialog-content>\n <ng-content></ng-content>\n </mat-dialog-content>\n @if (secondaryButtonLabel !== '' || primaryButtonLabel !== '') {\n <div mat-dialog-actions [class.dialog-action-w-message]=\"!!footerMessage\">\n @if (footerMessage) {\n <div class=\"actions-footer-message\">{{ footerMessage }}</div>\n }\n\n <!-- Projected footer content, rendered to the left of the action buttons -->\n <ng-content select=\"[dialogFooter]\"></ng-content>\n\n @if (!!secondaryButtonLabel) {\n <ui-button\n [companyColor]=\"companyColor\"\n [variant]=\"secondaryButtonType\"\n [applicationTheme]=\"applicationTheme\"\n [label]=\"secondaryButtonLabel\"\n [attr.data-testid]=\"secondaryButtonDataTestId\"\n (click)=\"onSecondaryButtonClick($event)\"\n ></ui-button>\n }\n @if (!!primaryButtonLabel) {\n <ui-button\n [iconPosition]=\"primaryButtonIconPosition()\"\n [iconName]=\"primaryButtonIconName ? primaryButtonIconName : undefined\"\n [variant]=\"primaryButtonType\"\n [disabled]=\"disableButtons || disablePrimaryButton\"\n [applicationTheme]=\"applicationTheme\"\n (click)=\"onPrimaryButtonClick($event)\"\n [label]=\"primaryButtonLabel\"\n [companyColor]=\"companyColor\"\n [attr.data-testid]=\"primaryButtonDataTestId\"\n ></ui-button>\n }\n </div>\n }\n\n <!-- This is announcement for screen reader that dialog was closed-->\n <div aria-live=\"assertive\" role=\"alert\" style=\"position: absolute; left: -9999px\">\n @if (modalClosed) {\n <span>{{ 'DIALOG.CLOSED' | uiTranslate | async }}</span>\n }\n </div>\n</div>\n","queryGuide":{"roles":["alert"],"ariaAttributes":["aria-live","aria-modal","aria-labelledby","aria-describedby"],"dataTestIds":[],"materialComponents":["mat-dialog-content"],"childUIComponents":["ui-button"],"suggestedQueries":["screen.getByRole('alert')"]},"filePath":"components/dialog/dialog.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1762-77327","lastModified":1786626756393.347},"confirm-dialog":{"name":"ConfirmDialogComponent","selector":"ui-confirm-dialog","category":"dialog","description":"A modal dialog that asks the user to confirm an irreversible or high-risk action before proceeding. Presents a title, a brief explanation of the consequence, and two actions — confirm and cancel. Used for deleting assessments, removing candidates, revoking team member access, and other destructive operations.","importPath":"@testgorilla/tgo-ui/components/confirm-dialog","moduleName":"ConfirmDialogComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"ConfirmDialogComponentModule","imports":["CommonModule","MatDialogModule","ButtonComponentModule","DialogComponentModule","UiTranslatePipe","ConfirmDialogComponent"],"exports":["ConfirmDialogComponent"],"declarations":[],"providers":["{ provide: MAT_DIALOG_DATA, useValue: {} }","{ provide: MatDialogRef, useValue: {} }"]},"types":{"typeAliases":[],"interfaces":[{"name":"ConfirmDialogData","properties":[{"name":"message","type":"string","optional":true},{"name":"confirmButtonText","type":"string","optional":true},{"name":"cancelButtonText","type":"string","optional":true},{"name":"title","type":"string","optional":true},{"name":"color","type":"string","optional":true},{"name":"language","type":"Language","optional":true},{"name":"messageHtml","type":"string","optional":true},{"name":"showCloseButton","type":"boolean","optional":true},{"name":"showSecondaryButton","type":"boolean","optional":true},{"name":"primaryButtonType","type":"ButtonColor","optional":true},{"name":"primaryButtonIconPosition","type":"ButtonIconPosition","optional":true},{"name":"secondaryButtonType","type":"ButtonColor","optional":true},{"name":"primaryButtonIconName","type":"IconName","optional":true},{"name":"primaryButtonDataTestId","type":"string","optional":true},{"name":"secondaryButtonDataTestId","type":"string","optional":true},{"name":"onConfirm","type":"() => void","optional":true}]}],"enums":[]},"examples":[],"documentation":"A modal dialog that asks the user to confirm an irreversible or high-risk action before proceeding. Presents a title, a brief explanation of the consequence, and two actions — confirm and cancel. Used for deleting assessments, removing candidates, revoking team member access, and other destructive operations.\n\n## When to use\n\n- **Describe the consequence in the body** — 'This will permanently delete the assessment and all its results' is more useful than 'Are you sure?'\n- **Label the confirm action with the specific verb** — 'Delete assessment', not just 'Confirm'\n- **Make cancel the default focus** — so accidental keypresses don't trigger the destructive action\n\n## When NOT to use\n\n- **Reversible actions** → use a Snackbar with an undo action instead\n- **Stacked confirmations** → redesign the flow so a single confirmation is sufficient\n- **Displaying information** → if there's nothing to confirm, use a standard Dialog\n","whenToUse":["**Describe the consequence in the body** — 'This will permanently delete the assessment and all its results' is more useful than 'Are you sure?'","**Label the confirm action with the specific verb** — 'Delete assessment', not just 'Confirm'","**Make cancel the default focus** — so accidental keypresses don't trigger the destructive action"],"whenNotToUse":["**Reversible actions** → use a Snackbar with an undo action instead","**Stacked confirmations** → redesign the flow so a single confirmation is sufficient","**Displaying information** → if there's nothing to confirm, use a standard Dialog"],"template":"<ng-container>\n <ui-dialog\n [title]=\"title ?? ('COMMON.CONFIRM' | uiTranslate | async)!\"\n [primaryButtonLabel]=\"confirmButtonText ?? ('COMMON.YES' | uiTranslate | async)!\"\n [secondaryButtonLabel]=\"\n showSecondaryButton ? (cancelButtonText ?? ('COMMON.CANCEL' | uiTranslate | async)!) : undefined\n \"\n (primaryButtonClickEvent)=\"onConfirm()\"\n (secondaryButtonClickEvent)=\"dialogRef.close(false)\"\n (closeEvent)=\"dialogRef.close(null)\"\n [primaryButtonType]=\"primaryButtonType\"\n [primaryButtonIconName]=\"primaryButtonIconName\"\n [secondaryButtonType]=\"secondaryButtonType\"\n [applicationTheme]=\"applicationTheme\"\n [showCloseButton]=\"showCloseButton\"\n [companyColor]=\"color\"\n [primaryButtonDataTestId]=\"primaryButtonDataTestId\"\n [secondaryButtonDataTestId]=\"secondaryButtonDataTestId\"\n [primaryButtonIconPosition]=\"primaryButtonIconPosition\"\n >\n <div class=\"message\">{{ confirmMessage }}</div>\n @if (confirmMessageHtml) {\n <div class=\"message\" [innerHTML]=\"confirmMessageHtml\"></div>\n }\n </ui-dialog>\n</ng-container>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-dialog"],"suggestedQueries":[]},"filePath":"components/confirm-dialog/confirm-dialog.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1762-77327","lastModified":1786626756392.7268},"dialog-custom":{"name":"DialogCustomComponent","selector":"ui-dialog-custom","category":"dialog","description":"A flexible modal dialog shell for building bespoke in-product overlay experiences that don't fit the Confirm Dialog or Media Dialog patterns. Provides the modal frame, header, and close button; the content is fully composable. Used for multi-field forms, selection flows, and complex interactions that require overlay focus.","importPath":"@testgorilla/tgo-ui/components/dialog-custom","moduleName":"DialogCustomComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"DialogCustomComponentModule","imports":["CommonModule","DialogComponentModule","ButtonComponentModule","ToggleComponentModule","DialogCustomComponent"],"exports":["DialogCustomComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A flexible modal dialog shell for building bespoke in-product overlay experiences that don't fit the Confirm Dialog or Media Dialog patterns. Provides the modal frame, header, and close button; the content is fully composable. Used for multi-field forms, selection flows, and complex interactions that require overlay focus.\n\n## When to use\n\n- **Focused task completion** — use when the interaction requires focus and the user needs to finish a task before returning to the main page\n- **Clear title and close control** — always include a visible title and a visible close control\n- **A single task per dialog** — for multiple distinct steps, consider a Side Panel with navigation instead\n\n## When NOT to use\n\n- **A page substitute** → dialogs shouldn't contain full navigation structures\n- **Nested dialogs** → dialogs inside dialogs create confusing layer management\n- **Destructive confirmations** → use Confirm Dialog, which has the correct action hierarchy built in\n\n## How to use\n\nTo use the Custom Dialog component, you need to import the `DialogCustomComponentModule` in your module:\n\n```typescript\nimport { DialogCustomComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n DialogCustomComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component with the DialogService:\n\n```typescript\nimport { DialogService } from '@testgorilla/tgo-ui';\n\n@Component({\n // ...\n})\nexport class YourComponent {\n constructor(private dialogService: DialogService) {}\n\n openCustomDialog(): void {\n const dialogConfig = {\n applicationTheme: 'light',\n size: 'small',\n extraData: {\n type: 'custom-header',\n },\n };\n\n this.dialogService\n .open(DialogCustomComponent, dialogConfig)\n .afterClosed()\n .subscribe(result => {\n // Handle dialog close\n });\n }\n}\n```\n","whenToUse":["**Focused task completion** — use when the interaction requires focus and the user needs to finish a task before returning to the main page","**Clear title and close control** — always include a visible title and a visible close control","**A single task per dialog** — for multiple distinct steps, consider a Side Panel with navigation instead"],"whenNotToUse":["**A page substitute** → dialogs shouldn't contain full navigation structures","**Nested dialogs** → dialogs inside dialogs create confusing layer management","**Destructive confirmations** → use Confirm Dialog, which has the correct action hierarchy built in"],"template":"<ui-dialog\n [title]=\"data.title\"\n [showCloseButton]=\"data.showCloseButton\"\n [applicationTheme]=\"applicationTheme\"\n [primaryButtonLabel]=\"'OK'\"\n [secondaryButtonLabel]=\"'Cancel'\"\n [canCloseFn]=\"canClose\"\n [keyboardOpen]=\"data.keyboardOpen\"\n [footerMessage]=\"data.footerMessage\"\n>\n <!-- Custom header content -->\n @if (data.type === 'custom-header') {\n <div dialogHeader class=\"custom-header\">\n <div class=\"button-group\">\n <ui-button label=\"Action 1\" [variant]=\"'ghost'\" [applicationTheme]=\"applicationTheme\"></ui-button>\n <ui-button label=\"Action 2\" [variant]=\"'primary'\" [applicationTheme]=\"applicationTheme\"></ui-button>\n </div>\n </div>\n }\n\n <!-- Custom footer content projection -->\n @if (data.type === 'footer-content') {\n <ui-toggle dialogFooter label=\"Don't show this again\" [applicationTheme]=\"applicationTheme\"></ui-toggle>\n }\n\n <!-- Dialog content -->\n <div class=\"dialog-content\">\n @if (data.type === 'default') {\n <p class=\"dialog-message\">This is a default dialog example with a standard header.</p>\n }\n @if (data.type === 'custom-header') {\n <p class=\"dialog-message\">This dialog uses a custom header with content projection.</p>\n }\n @if (data.type === 'footer-content') {\n <p class=\"dialog-message\">\n This dialog projects a <code>ui-toggle</code> into the footer, to the left of the action buttons.\n </p>\n }\n <p class=\"dialog-message\">The dialog will be closeable in 2 seconds (demonstrating canCloseFn).</p>\n </div>\n</ui-dialog>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-dialog","ui-button","ui-toggle"],"suggestedQueries":[]},"filePath":"components/dialog-custom/dialog-custom.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1762-77327","lastModified":1786626756393.048},"divider":{"name":"DividerComponent","selector":"ui-divider","category":"divider","description":"A horizontal rule that separates sections of content within a layout. Available in two sizes (small — the default — and large) and an optional labelled variant for inline separators like 'OR' in authentication flows. Keeps layouts readable without adding visual weight.","importPath":"@testgorilla/tgo-ui/components/divider","moduleName":"DividerComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"DividerSize","defaultValue":"'small'","required":false,"alias":null,"transform":null,"description":"Divider size. Defaults to 'small'.","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"'#46A997'","required":false,"alias":null,"transform":null,"description":"Determines whether the checkbox color.\nDefault: Test Gorilla primary color.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Text content in the center of the divider","isSignal":false}],"outputs":[],"module":{"name":"DividerComponentModule","imports":["CommonModule","MatDividerModule","DividerComponent"],"exports":["DividerComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"DividerSize","value":"'small' | 'large'"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A horizontal rule that separates sections of content within a layout. Available in two sizes (small — the default — and large) and an optional labelled variant for inline separators like 'OR' in authentication flows. Keeps layouts readable without adding visual weight.\n\n## When to use\n\n- **Separating logically distinct sections** within a card or page region — not just for decoration\n- **The With Label variant in auth flows** → use it (e.g. between 'Sign in with Google' and the email form) to convey the 'OR' relationship clearly\n- **The small size inside compact components** like dropdown panels or overflow menus\n\n## When NOT to use\n\n- **Padding out sparse layouts** → if content needs a divider to feel structured, the layout may need rethinking\n- **Stacking multiple Dividers in sequence** → if sections are that distinct, they likely need separate Cards\n- **As a footer border inside a Card** → use the Card's own padding and spacing instead\n\n## How to use\n\nTo use the Divider component, you need to import the `DividerComponentModule` in your module:\n\n```typescript\nimport { DividerComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n DividerComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-divider [size]=\"'large'\" [applicationTheme]=\"theme\" [label]=\"'Optional Label'\"> </ui-divider>\n```\n","whenToUse":["**Separating logically distinct sections** within a card or page region — not just for decoration","**The With Label variant in auth flows** → use it (e.g. between 'Sign in with Google' and the email form) to convey the 'OR' relationship clearly","**The small size inside compact components** like dropdown panels or overflow menus"],"whenNotToUse":["**Padding out sparse layouts** → if content needs a divider to feel structured, the layout may need rethinking","**Stacking multiple Dividers in sequence** → if sections are that distinct, they likely need separate Cards","**As a footer border inside a Card** → use the Card's own padding and spacing instead"],"template":"<div class=\"ui-divider-wrapper\">\n @if (!label) {\n <mat-divider aria-hidden=\"true\" [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider>\n } @else {\n <div class=\"container\">\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n <div [class]=\"'label' + ' ' + size\">{{ label }}</div>\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n </div>\n }\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-hidden"],"dataTestIds":[],"materialComponents":["mat-divider"],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/divider/divider.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=222-1054","lastModified":1786626756393.9036},"donut-chart":{"name":"DonutChartComponent","selector":"ui-donut-chart","category":"charts","description":"A circular chart that shows part-to-whole relationships between a small number of categories. Used in assessment analytics dashboards to visualise candidate score distributions, pass/fail ratios, and test completion breakdowns at a glance.","importPath":"@testgorilla/tgo-ui/components/donut-chart","moduleName":"DonutChartComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"donutChartData","type":"DonutChartData","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[],"module":{"name":"DonutChartComponentModule","imports":["BaseChartDirective","DonutChartComponent"],"exports":["DonutChartComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"DonutChartData","value":"{\n labels: string[];\n data: number[];\n backgroundColor?: Color[];\n}"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A circular chart that shows part-to-whole relationships between a small number of categories. Used in assessment analytics dashboards to visualise candidate score distributions, pass/fail ratios, and test completion breakdowns at a glance.\n\n## When to use\n\n- **2–4 categories maximum** — donut charts with more slices become hard to read\n- **A legend or direct labels** — so the chart isn't dependent on colour alone for interpretation\n- **Percentage values in the labels** — when the proportional relationship is the message\n\n## When NOT to use\n\n- **Values that are too similar** → slices of equal size are indistinguishable\n- **Sequential or time-series data** → use a line chart or bar chart instead\n- **The only data representation** → pair with a data table or summary numbers for accessibility\n","whenToUse":["**2–4 categories maximum** — donut charts with more slices become hard to read","**A legend or direct labels** — so the chart isn't dependent on colour alone for interpretation","**Percentage values in the labels** — when the proportional relationship is the message"],"whenNotToUse":["**Values that are too similar** → slices of equal size are indistinguishable","**Sequential or time-series data** → use a line chart or bar chart instead","**The only data representation** → pair with a data table or summary numbers for accessibility"],"template":"<div style=\"height: 200px\">\n <canvas baseChart [data]=\"chartData()\" type=\"doughnut\" [options]=\"donutChartOptions\" [plugins]=\"donutChartPlugins\">\n </canvas>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/donut-chart/donut-chart.component.ts","supportsTheme":true,"lastModified":1786626756394.2498},"dropdown":{"name":"DropdownComponent","selector":"ui-dropdown","category":"forms","description":"A select input that opens a list of options in a panel below the trigger. Visually it renders as an outlined Material form field (`mat-form-field` with `appearance=\"outline\"`): a rounded rectangular box with a floating label and a chevron on the right. Supports default, hover, selected, filled, error, and disabled states. Used for single-value selections across assessment settings, account configuration, and any form field with a defined, bounded set of options.","importPath":"@testgorilla/tgo-ui/components/dropdown","moduleName":"DropdownComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Text content will be applied to the input element if present.","isSignal":false},{"name":"labelIcon","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Icon to display alongside the label","isSignal":false},{"name":"isAIVariant","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicates if this field is filled by AI","isSignal":false},{"name":"name","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input name attribute","isSignal":false},{"name":"placeholder","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Placeholder to be shown if no value has been selected.","isSignal":false},{"name":"id","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Unique id of the element.","isSignal":false},{"name":"value","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input value","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Whether the component is in an error state.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the component is disabled.\nDefault: false.","isSignal":false},{"name":"valueList","type":"OptionType[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"List of options.","isSignal":false},{"name":"allowClear","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Whether the user should be allowed to clear the values.\nDefault: true.","isSignal":false},{"name":"allowMultipleSelection","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"This will allow the user to select multiple values at once.\nDefault: true.","isSignal":false},{"name":"required","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the component is required.\nDefault: false.","isSignal":false},{"name":"showBottomContent","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show reserved content below form field","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"textField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"valueField","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"hasError","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"companyColor","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the option hover.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"panelClass","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Class to be applied to the panel.","isSignal":false}],"outputs":[{"name":"closed","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"opened","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"DropdownComponentModule","imports":["CommonModule","MatFormFieldModule","MatInputModule","IconComponentModule","FormsModule","ReactiveFormsModule","ButtonComponentModule","MatSelectModule","UiTranslatePipe","ValidationErrorModule","HasValidationErrorPipe","DropdownComponent"],"exports":["DropdownComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"OptionType","value":"{\n value?: string;\n displayValue?: string;\n sectionTitle?: string;\n isAISuggested?: boolean;\n} & Record<string, unknown>"}],"interfaces":[],"enums":[]},"examples":[{"name":"WithCustomTemplate","template":"<ui-dropdown\n [label]=\"label\"\n [placeholder]=\"placeholder\"\n [valueList]=\"valueList\"\n [valueField]=\"valueField\"\n applicationTheme=\"${appTheme}\">\n <ng-template #customSelectedOptionTemplate let-option>\n <div>\n <div>\n Selected: {{ option?.name }}\n </div>\n </div>\n </ng-template>\n\n <ng-template #customOptionTemplate let-option>\n <div>\n <div>\n <div>{{ option.name }} - {{ option.description }}</div>\n </div>\n </div>\n </ng-template>\n </ui-dropdown>","props":{}}],"documentation":"A select input that opens a list of options in a panel below the trigger. Visually it renders as an outlined Material form field (`mat-form-field` with `appearance=\"outline\"`): a rounded rectangular box with a floating label and a chevron on the right. Supports default, hover, selected, filled, error, and disabled states. Used for single-value selections across assessment settings, account configuration, and any form field with a defined, bounded set of options.\n\n## When to use\n\n- **Fixed option set of 4–15 items** — reach for Dropdown when the choices are bounded and won't grow unpredictably\n- **Always show a placeholder** ('Select a role', 'Choose language') that disappears once a value is selected\n- **Pair the error state with an adjacent error message** — don't rely on the border colour alone\n\n## When NOT to use\n\n- **Fewer than 4 options** → use Radio Buttons so all options stay visible\n- **Multi-select** → use Autocomplete in multi mode\n- **Navigation** → it's a form control, not a menu\n\n## How to use\n\nTo use the Dropdown component, you need to import the `DropdownComponentModule` in your module:\n\n```typescript\nimport { DropdownComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n DropdownComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates. The dropdown implements `ControlValueAccessor`, so the selected value is read and written through Angular forms — with `[(ngModel)]`:\n\n```html\n<ui-dropdown\n label=\"Country\"\n placeholder=\"Select a country\"\n [valueList]=\"countries\"\n [allowMultipleSelection]=\"false\"\n applicationTheme=\"light\"\n [(ngModel)]=\"selectedCountry\"\n>\n</ui-dropdown>\n```\n\nOr with reactive forms:\n\n```html\n<form [formGroup]=\"form\">\n <ui-dropdown label=\"Country\" placeholder=\"Select a country\" [valueList]=\"countries\" formControlName=\"country\">\n </ui-dropdown>\n</form>\n```\n\n> **Note:** there is no `(valueChange)` output — the only outputs are `(opened)` and `(closed)`, which fire when the options panel opens/closes. To react to selection changes, use `[(ngModel)]` / `ngModelChange` or subscribe to the form control's `valueChanges`.\n\nFor custom display values:\n\n```html\n<ui-dropdown label=\"Country\" [valueList]=\"countries\" textField=\"name\" valueField=\"code\" applicationTheme=\"light\">\n</ui-dropdown>\n```\n\nThe dropdown component supports single and multiple selection, sections, custom fields, and custom templates.\n","whenToUse":["**Fixed option set of 4–15 items** — reach for Dropdown when the choices are bounded and won't grow unpredictably","**Always show a placeholder** ('Select a role', 'Choose language') that disappears once a value is selected","**Pair the error state with an adjacent error message** — don't rely on the border colour alone"],"whenNotToUse":["**Fewer than 4 options** → use Radio Buttons so all options stay visible","**Multi-select** → use Autocomplete in multi mode","**Navigation** → it's a form control, not a menu"],"template":"<ng-container>\n <mat-form-field\n appearance=\"outline\"\n [attr.theme]=\"applicationTheme\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) || hasError ? 'warn' : 'accent'\"\n [ngClass]=\"{\n 'hide-bottom-content': !showBottomContent,\n 'without-label': !label,\n 'keyboard-focused': keyboardFocused(),\n 'has-label': !!label,\n }\"\n >\n <mat-label class=\"label-with-icon\">\n @if (isAIVariant || labelIcon) {\n <ui-icon [name]=\"isAIVariant ? 'Sparkle-in-line' : labelIcon!\" [size]=\"'16'\" class=\"label-icon\"></ui-icon>\n }\n {{ label }}\n @if (required) {\n <span>*</span>\n }\n </mat-label>\n <mat-select\n (closed)=\"onClosed()\"\n (opened)=\"onOpened()\"\n #matSelect\n [id]=\"id!\"\n [disableRipple]=\"applicationTheme !== 'classic'\"\n [panelClass]=\"'ui-dropdown-list' + ' ' + applicationTheme + (panelClass ? ' ' + panelClass : '')\"\n [disabled]=\"disabled\"\n [multiple]=\"!!allowMultipleSelection\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n (selectionChange)=\"onChangeOption($event)\"\n [attr.name]=\"name\"\n data-testid=\"ui-dropdown-select\"\n >\n @if (customSelectedOptionTemplate && valueField) {\n <mat-select-trigger>\n <ng-container\n [ngTemplateOutlet]=\"customSelectedOptionTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: valueList.find(findValueByValueField) }\"\n ></ng-container>\n </mat-select-trigger>\n }\n\n @if (customOptionTemplate && valueField) {\n @for (item of valueList; track item[valueField]) {\n <mat-option [value]=\"item[valueField]\">\n <ng-container\n [ngTemplateOutlet]=\"customOptionTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n ></ng-container>\n </mat-option>\n }\n } @else {\n @for (section of valueList; track section) {\n <div class=\"mat-option-section\">\n @if (section.sectionTitle) {\n <span class=\"section-title\">{{ section.sectionTitle }}</span>\n }\n @if (section) {\n <mat-option\n #matOption\n [value]=\"valueField ? section[valueField] : textField && !valueField ? section : section.value\"\n >\n <div class=\"option-content\">\n <span class=\"option-text\">{{\n textField ? section[textField] : (section.displayValue ?? section.value)\n }}</span>\n @if (section.isAISuggested) {\n <ui-icon [name]=\"'Sparkle-in-line'\" [size]=\"'16'\"> </ui-icon>\n }\n </div>\n </mat-option>\n }\n </div>\n }\n }\n </mat-select>\n @if (allowClear && !matSelect.empty) {\n <ui-button\n [ariaLabel]=\"('COMMON.CLEAR' | uiTranslate | async)!\"\n variant=\"secondary\"\n [justIcon]=\"true\"\n class=\"clear\"\n [iconName]=\"applicationTheme === 'classic' ? 'Close' : 'Close-filled'\"\n [label]=\"('COMMON.CLEAR' | uiTranslate | async)!\"\n (click)=\"clearValue($event)\"\n ></ui-button>\n }\n @if (!matSelect.panelOpen) {\n <ui-icon\n class=\"chevron-icon\"\n size=\"24\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Arrow_down' : 'Arrow-chevron-down-filled'\"\n ></ui-icon>\n }\n @if (matSelect.panelOpen) {\n <ui-icon\n class=\"chevron-icon\"\n size=\"24\"\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Arrow_up' : 'Arrow-chevron-up-filled'\"\n ></ui-icon>\n }\n @if (errorsLength || (ngControl?.errors | hasValidationError)) {\n <mat-hint class=\"error\">\n @if (errorsLength) {\n @for (error of errors; track error) {\n <div class=\"errors\">\n <ui-icon\n [applicationTheme]=\"applicationTheme\"\n [name]=\"applicationTheme === 'classic' ? 'Error' : 'Error-in-line'\"\n ></ui-icon\n >{{ error }}\n </div>\n }\n }\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error [ngControl]=\"ngControl\" [label]=\"hideLabelInErrors ? null : label\"></ui-validation-error>\n }\n </mat-hint>\n }\n </mat-form-field>\n</ng-container>\n","queryGuide":{"roles":["combobox","option"],"ariaAttributes":[],"dataTestIds":["ui-dropdown-select"],"materialComponents":["mat-form-field","mat-label","mat-select","mat-select-trigger","mat-option","mat-hint"],"childUIComponents":["ui-icon","ui-button","ui-validation-error"],"suggestedQueries":["screen.getByRole('combobox')","screen.getByRole('option')","// Open dropdown: await userEvent.click(screen.getByRole('combobox'))","// Select option: await userEvent.click(await screen.findByText('Option'))","screen.getByTestId('ui-dropdown-select')"]},"filePath":"components/dropdown/dropdown.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=691-11509","lastModified":1786626756394.6772},"elevation-shadow":{"name":"ElevationShadowComponent","selector":"ui-elevation-shadow","category":"elevation-shadow","description":"A utility component that applies Canopy-UI's standardised shadow tokens to any surface. Provides four shadow levels — page card, hover, modal, and notification — matching the elevation system defined in the design tokens. Use it to ensure shadow consistency without hardcoding box-shadow values.","importPath":"@testgorilla/tgo-ui/components/elevation-shadow","moduleName":"ElevationShadowComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"elevationType","type":"Elevation","defaultValue":"ElevationType.DEFAULT","required":false,"alias":null,"transform":null,"description":"The type of elevation shadow.\nDefault: default.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"ElevationShadowComponentModule","imports":["CommonModule","ElevationShadowComponent"],"exports":["ElevationShadowComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"Default","template":"<ui-elevation-shadow ${argsToTemplate(args)} [applicationTheme]=\"appTheme\">\n <p class=\"text\"></p>\n </ui-elevation-shadow>","props":{"elevationType":"ElevationType.DEFAULT"}},{"name":"Modal","template":"<ui-elevation-shadow ${argsToTemplate(args)} [applicationTheme]=\"appTheme\">\n <p class=\"text\"></p>\n </ui-elevation-shadow>","props":{"elevationType":"ElevationType.MODAL"}}],"documentation":"A utility component that applies Canopy-UI's standardised shadow tokens to any surface. Provides four shadow levels — page card, hover, modal, and notification — matching the elevation system defined in the design tokens. Use it to ensure shadow consistency without hardcoding box-shadow values.\n\n## When to use\n\n- **Declarative shadows** — apply shadows declaratively rather than writing custom CSS box-shadow values\n- **Match level to elevation** — use page-card for default cards, hover for hovered surfaces, modal for dialogs, and notification for snackbars\n- **Storybook previews** — demonstrate elevation differences between component states\n\n## When NOT to use\n\n- **Decorative shadows** → every shadow should communicate a genuine elevation change, not aesthetic interest\n- **Stacking levels** → don't combine multiple shadow levels on a single surface; one surface, one elevation\n- **Inline elements or text** → shadows belong on surfaces and containers only\n\n## How to use\n\nTo use the Elevation Shadow component, you need to import the `ElevationShadowComponentModule` in your module:\n\n```typescript\nimport { ElevationShadowComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ElevationShadowComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-elevation-shadow [elevationType]=\"'default'\" [applicationTheme]=\"theme\">\n <div>Content goes here</div>\n</ui-elevation-shadow>\n```\n","whenToUse":["**Declarative shadows** — apply shadows declaratively rather than writing custom CSS box-shadow values","**Match level to elevation** — use page-card for default cards, hover for hovered surfaces, modal for dialogs, and notification for snackbars","**Storybook previews** — demonstrate elevation differences between component states"],"whenNotToUse":["**Decorative shadows** → every shadow should communicate a genuine elevation change, not aesthetic interest","**Stacking levels** → don't combine multiple shadow levels on a single surface; one surface, one elevation","**Inline elements or text** → shadows belong on surfaces and containers only"],"template":"<div class=\"elevation-shadow\" [ngClass]=\"elevationType\" [attr.theme]=\"applicationTheme\">\n <ng-content></ng-content>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/elevation-shadow/elevation-shadow.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=23671-9754","lastModified":1786626756395.1914},"empty-state":{"name":"EmptyStateComponent","selector":"ui-empty-state","category":"empty-state","description":"A placeholder view shown when a list, table, or section has no content to display. Used when a hiring manager has no assessments yet, a candidate list is empty, or a filter returns zero results. A well-crafted empty state turns a dead end into a next step.","importPath":"@testgorilla/tgo-ui/components/empty-state","moduleName":"EmptyStateComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"showIllustration","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"illustrationVariant","type":"IllustrationVariant","defaultValue":"'Empty-search'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"icon","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"emptyStateVariant","type":"EmptyStateVariant","defaultValue":"'desktop'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"title","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"bodyText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"primaryButtonText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"primaryButtonVariant","type":"ButtonVariant","defaultValue":"'pink'","required":false,"alias":null,"transform":null,"description":"Primary button variant - only for new theme.","isSignal":false},{"name":"generalButtonVariation","type":"ButtonColor","defaultValue":"'primary'","required":false,"alias":null,"transform":null,"description":"General button variation - only for new theme.","isSignal":false},{"name":"primaryButtonIconName","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"primaryButtonIconPosition","type":"ButtonIconPosition","defaultValue":"'left'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"secondaryButtonText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"secondaryButtonVariant","type":"ButtonVariant","defaultValue":"'black'","required":false,"alias":null,"transform":null,"description":"Secondary button variant - only for new theme.","isSignal":false},{"name":"secondaryButtonIconName","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"secondaryButtonIconPosition","type":"ButtonIconPosition","defaultValue":"'left'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"tertiaryButtonText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"tertiaryButtonVariant","type":"ButtonVariant","defaultValue":"'ghost'","required":false,"alias":null,"transform":null,"description":"Tertiary button variant - only for new theme.","isSignal":false},{"name":"tertiaryButtonIconName","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"tertiaryButtonIconPosition","type":"ButtonIconPosition","defaultValue":"'left'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"generalVariationButtonText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"generalVariationButtonSize","type":"ButtonSize","defaultValue":"'big'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"generalVariationButtonIconName","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"generalVariationButtonIconPosition","type":"ButtonIconPosition","defaultValue":"'left'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"generalVariationButtonDisabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[{"name":"primaryButtonClick","eventType":"Event","alias":null,"description":"Event triggered when the primary button is clicked.","isSignal":false},{"name":"secondaryButtonClick","eventType":"Event","alias":null,"description":"Event triggered when the secondary button is clicked.","isSignal":false},{"name":"tertiaryButtonClick","eventType":"Event","alias":null,"description":"Event triggered when the tertiary button is clicked.","isSignal":false},{"name":"generalVariationButtonClick","eventType":"Event","alias":null,"description":"Event triggered when the general variation button is clicked.","isSignal":false}],"module":{"name":"EmptyStateComponentModule","imports":["CommonModule","IconComponentModule","NgOptimizedImage","ButtonComponentModule","UiTranslatePipe","EmptyStateComponent"],"exports":["EmptyStateComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"ButtonVariant","value":"'pink' | 'black' | 'ghost'"},{"name":"IllustrationVariant","value":"'Empty-search' | 'Lost-found-404' | 'Disconnected' | 'Messages' | 'Applications'"},{"name":"EmptyStateVariant","value":"'mobile' | 'desktop'"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A placeholder view shown when a list, table, or section has no content to display. Used when a hiring manager has no assessments yet, a candidate list is empty, or a filter returns zero results. A well-crafted empty state turns a dead end into a next step.\n\n## When to use\n\n- **Primary action** — always include a primary action in the empty state, such as 'Create your first assessment' or 'Invite a candidate'\n- **Right kind of empty** — differentiate between 'nothing exists yet' (onboarding empty state with illustration) and 'nothing matches your filter' (simpler message with a clear filter reset)\n- **Context-specific message** — keep the message specific to the context, as 'No candidates yet' is more useful than 'No results'\n\n## When NOT to use\n\n- **A blank empty state** → don't show one with no message or action, as it looks broken\n- **Reused copy everywhere** → each surface deserves a message relevant to what's missing, not the same text repeated\n- **Multiple CTAs** → don't overload the empty state, as one clear next step is enough\n\n## How to use\n\nTo use the Empty State component, you need to import the `EmptyStateComponentModule` in your module:\n\n```typescript\nimport { EmptyStateComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n EmptyStateComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-empty-state\n [title]=\"'No results found'\"\n [bodyText]=\"'Try a different search term or adjust your filters.'\"\n [showIllustration]=\"true\"\n [illustrationVariant]=\"'Empty-search'\"\n [primaryButtonText]=\"'Clear filters'\"\n [applicationTheme]=\"theme\"\n (primaryButtonClick)=\"onPrimaryButtonClick($event)\"\n>\n</ui-empty-state>\n```\n","whenToUse":["**Primary action** — always include a primary action in the empty state, such as 'Create your first assessment' or 'Invite a candidate'","**Right kind of empty** — differentiate between 'nothing exists yet' (onboarding empty state with illustration) and 'nothing matches your filter' (simpler message with a clear filter reset)","**Context-specific message** — keep the message specific to the context, as 'No candidates yet' is more useful than 'No results'"],"whenNotToUse":["**A blank empty state** → don't show one with no message or action, as it looks broken","**Reused copy everywhere** → each surface deserves a message relevant to what's missing, not the same text repeated","**Multiple CTAs** → don't overload the empty state, as one clear next step is enough"],"template":"<ng-container>\n <div class=\"empty-state-container\" [attr.theme]=\"applicationTheme\" [class.mobile]=\"emptyStateVariant === 'mobile'\">\n @if (showIllustration) {\n <div class=\"illustration\" [attr.aria-hidden]=\"true\">\n @if (applicationTheme === 'classic') {\n <img [attr.src]=\"'/images/empty-state-default.svg'\" [width]=\"160\" [height]=\"160\" alt=\"illustration\" />\n }\n @if (applicationTheme !== 'classic') {\n <img\n [attr.src]=\"'/icons/rebrand/' + illustrationVariant + '-in-line.svg'\"\n [width]=\"160\"\n [height]=\"160\"\n alt=\"illustration\"\n />\n }\n </div>\n }\n\n @if (icon) {\n <div class=\"icon\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"icon\" size=\"40\"></ui-icon>\n </div>\n }\n\n <div class=\"text-content\">\n <div class=\"title\">{{ title ? title : ('COMMON.NO_RESULTS' | uiTranslate | async) }}</div>\n @if (bodyText) {\n <div class=\"body-text\" [innerHTML]=\"bodyText\"></div>\n }\n </div>\n\n <div class=\"actions\">\n @if (primaryButtonText) {\n <ui-button\n [fullWidth]=\"emptyStateVariant === 'mobile'\"\n [applicationTheme]=\"buttonApplicationTheme\"\n class=\"action-button\"\n [variant]=\"applicationTheme !== 'classic' ? getVariant(primaryButtonVariant) : 'primary'\"\n [label]=\"primaryButtonText\"\n [iconName]=\"primaryButtonIconName\"\n [iconPosition]=\"primaryButtonIconPosition\"\n (buttonClickEvent)=\"onPrimaryButtonClick($event)\"\n ></ui-button>\n }\n @if (secondaryButtonText) {\n <ui-button\n [fullWidth]=\"emptyStateVariant === 'mobile'\"\n [applicationTheme]=\"buttonApplicationTheme\"\n class=\"action-button\"\n [variant]=\"applicationTheme !== 'classic' ? getVariant(secondaryButtonVariant) : 'secondary'\"\n [label]=\"secondaryButtonText\"\n [iconName]=\"secondaryButtonIconName\"\n [iconPosition]=\"secondaryButtonIconPosition\"\n (buttonClickEvent)=\"onSecondaryButtonClick($event)\"\n ></ui-button>\n }\n @if (tertiaryButtonText) {\n <ui-button\n [fullWidth]=\"emptyStateVariant === 'mobile'\"\n [applicationTheme]=\"buttonApplicationTheme\"\n class=\"action-button\"\n [variant]=\"applicationTheme !== 'classic' ? getVariant(tertiaryButtonVariant) : 'tertiary'\"\n [label]=\"tertiaryButtonText\"\n [iconName]=\"tertiaryButtonIconName\"\n [iconPosition]=\"tertiaryButtonIconPosition\"\n (buttonClickEvent)=\"onTertiaryButtonClick($event)\"\n ></ui-button>\n }\n @if (generalVariationButtonText) {\n <ui-button\n class=\"action-button\"\n [fullWidth]=\"emptyStateVariant === 'mobile'\"\n [applicationTheme]=\"buttonApplicationTheme\"\n [variant]=\"generalButtonVariation\"\n [label]=\"generalVariationButtonText\"\n [iconName]=\"generalVariationButtonIconName\"\n [iconPosition]=\"generalVariationButtonIconPosition\"\n [disabled]=\"generalVariationButtonDisabled\"\n [size]=\"generalVariationButtonSize\"\n (buttonClickEvent)=\"onGeneralVariationButtonClick($event)\"\n ></ui-button>\n }\n </div>\n </div>\n</ng-container>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-hidden"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon","ui-button"],"suggestedQueries":[]},"filePath":"components/empty-state/empty-state.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1898-13112","lastModified":1786626756395.824},"field":{"name":"FieldComponent","selector":"ui-field","category":"forms","description":"The standard single-line text input for structured data entry. Supports all standard states (default, hover, focus, filled, error, disabled) and is used across the platform for name fields, email inputs, URLs, and any short free-text entry. The foundation of all Canopy-UI form layouts.","importPath":"@testgorilla/tgo-ui/components/field","moduleName":"FieldComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"fullWidth","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Indicator of the autocomplete width","isSignal":false},{"name":"fullHeight","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator of the field height","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Form field label","isSignal":false},{"name":"labelIcon","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Icon to display alongside the label","isSignal":false},{"name":"fieldName","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input name attribute","isSignal":false},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input placeholder","isSignal":false},{"name":"value","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Input value","isSignal":false},{"name":"badgeVariant","type":"BadgeVariant | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Variant of badge to use","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if input is disabled","isSignal":false},{"name":"required","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input is required or not","isSignal":false},{"name":"readOnly","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input is readonly or not","isSignal":false},{"name":"hintMessage","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Hint text","isSignal":false},{"name":"type","type":"FieldType","defaultValue":"'text'","required":false,"alias":null,"transform":null,"description":"Input type","isSignal":false},{"name":"updateOnBlur","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input is update only on blur","isSignal":false},{"name":"allowOnlyDigits","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"allow only digits in input","isSignal":false},{"name":"isAutocompleteOff","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"allow autocomplete off in input","isSignal":false},{"name":"allowNegative","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"allow negative numbers","isSignal":false},{"name":"showBottomContent","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show reserved content below form field","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show loading button. Only for text","isSignal":false},{"name":"isValid","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show icon when field is valid","isSignal":false},{"name":"maxCharacters","type":"number","defaultValue":"255","required":false,"alias":null,"transform":null,"description":"Max characters number","isSignal":false},{"name":"trimOnBlur","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Trim text on blur","isSignal":false},{"name":"trimOnSubmit","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Trim text on submit","isSignal":false},{"name":"maxRows","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Max rows for multi-line type","isSignal":false},{"name":"hasTextAreaCounter","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"has text area counter","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"max","type":"number | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Set maximum number","isSignal":false},{"name":"min","type":"number | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Set minimum number","isSignal":false},{"name":"textareaHeight","type":"number","defaultValue":"110","required":false,"alias":null,"transform":null,"description":"Sets textarea height. Default 110","isSignal":false},{"name":"borderless","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Sets textarea borderless. Default false","isSignal":false},{"name":"autosizableTextarea","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"isAIVariant","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicates if this field is filled by AI","isSignal":false},{"name":"hasError","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show state of error","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input field errors","isSignal":false},{"name":"labelHtml","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Form field label from html","isSignal":true},{"name":"id","type":"unknown","defaultValue":"`field-${fieldID++}`","required":false,"alias":null,"transform":null,"description":"Input id","isSignal":true},{"name":"ariaLabelledby","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used for input field Aria LabeledBy Text.","isSignal":true},{"name":"ariaDescribedby","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used for input field Aria DescribedBy Text.","isSignal":true}],"outputs":[{"name":"validateEvent","eventType":"string","alias":null,"description":"","isSignal":false},{"name":"fieldBlur","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"FieldComponentModule","imports":["CommonModule","MatFormFieldModule","MatInputModule","IconComponentModule","FormsModule","ReactiveFormsModule","ButtonComponentModule","MatIconModule","UiTranslatePipe","ValidationErrorModule","HasValidationErrorPipe","DigitsOnlyDirective","BadgeComponentModule","FieldComponent"],"exports":["FieldComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"FieldType","value":"| 'text'\n | 'password'\n | 'email'\n | 'number'\n | 'tel'\n | 'search'\n | 'collapsed-search'\n | 'validation-text'\n | 'textarea'\n | 'textarea-scrollable'\n | 'multi-line'"}],"interfaces":[],"enums":[]},"examples":[{"name":"FullHeightTextarea","template":"<div style=\"height: 400px; border: 1px dashed #ccc; padding: 16px;\">\n <ui-field\n [type]=\"type\"\n [label]=\"label\"\n [placeholder]=\"placeholder\"\n [fullHeight]=\"fullHeight\"\n [hasTextAreaCounter]=\"hasTextAreaCounter\"\n [value]=\"value\"\n [applicationTheme]=\"applicationTheme\">\n </ui-field>\n </div>","props":{"type":"'multi-line'","label":"'Full Height Textarea'","placeholder":"'Enter your text here...'","fullHeight":"true","hasTextAreaCounter":"true","value":"'This textarea will expand to fill the full height of its container. Perfect for forms where you need a large text input area that adapts to the available space.'"}}],"documentation":"The standard single-line text input for structured data entry. Supports all standard states (default, hover, focus, filled, error, disabled) and is used across the platform for name fields, email inputs, URLs, and any short free-text entry. The foundation of all Canopy-UI form layouts.\n\n## When to use\n\n- **Always include a label** above the field — never rely on a placeholder as the label\n- **Helper text below the field** for format guidance (e.g. 'Enter the full URL including https://')\n- **Trigger the error state on blur or form submission** — not on every keystroke while the user is typing\n\n## When NOT to use\n\n- **Multi-line text** → use a textarea variant instead\n- **Structured data with a defined format** (dates, phone numbers, currency) → use the specialised input component\n- **Hiding the label on focus** → always keep it visible so the user knows what they're filling in\n- **An AI button bolted below a textarea** → AI generate/assist affordances live inside the text area; use the Write with AI component (see 'AI-assisted input' below)\n\n## How to use\n\nTo use the Field component, you need to import the `FieldComponentModule` in your module:\n\n```typescript\nimport { FieldComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n FieldComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\n## Basic Usage\n\nYou can use the component in your templates:\n\n```html\n<ui-field type=\"text\" label=\"Username\" placeholder=\"Enter your username\"> </ui-field>\n```\n\n## Field Types\n\nThe Field component supports various input types:\n\n- **text**: Regular text input\n- **password**: Password input with show/hide toggle\n- **search**: Search input with clear button\n- **collapsed-search**: Collapsible search input\n- **email**: Email input with validation\n- **number**: Numeric input (can be restricted to digits only)\n- **tel**: Telephone number input\n- **textarea**: Multi-line text area\n- **textarea-scrollable**: Scrollable text area with custom scrollbar styling\n- **multi-line**: Another multi-line input option\n- **validation-text**: Text input with validation display\n\n## AI-assisted input\n\nDesign rule: AI generate/assist affordances live **inside** the text area — never as a separate button below it. Don't compose a `ui-field` textarea with an AI `ui-button` bolted underneath.\n\nFor AI-assisted long text, use the Write with AI component. In its embedded (default) mode it hosts the textarea itself and renders the \"AI assist\" affordance inside the field's frame:\n\n```html\n<ui-write-with-ai\n fieldLabel=\"Job description\"\n [tags]=\"refineTags\"\n [value]=\"description\"\n (valueChange)=\"description = $event\"\n (promptSubmit)=\"onPromptSubmit($event)\"\n></ui-write-with-ai>\n```\n\nFor a field whose value was filled by AI, use `isAIVariant` — it marks the field with the AI sparkle icon in the label:\n\n```html\n<ui-field type=\"textarea\" label=\"Suggested description\" [isAIVariant]=\"true\"></ui-field>\n```\n\n## Features\n\n- **Form integration**: Works with both Template-driven and Reactive forms\n- **Validation**: Displays error messages\n- **Accessibility**: Supports ARIA attributes for accessibility\n- **Theming**: Supports different application themes (light/dark/classic)\n- **Configurable**: Numerous input options for customization\n\n## Examples\n\n### Text input with label and hint\n\n```html\n<ui-field\n type=\"text\"\n label=\"First Name\"\n placeholder=\"Enter your first name\"\n hintMessage=\"Your first name as it appears on your ID\"\n>\n</ui-field>\n```\n\n### Text input with label from html\n\n```html\n<ui-field type=\"text\" labelHtml=\"First <b>Name</b>\" placeholder=\"Enter your first name\"> </ui-field>\n```\n\n### Password input\n\n```html\n<ui-field type=\"password\" label=\"Password\" placeholder=\"Enter your password\"> </ui-field>\n```\n\n### Number input with restrictions\n\n```html\n<ui-field\n type=\"number\"\n label=\"Age\"\n placeholder=\"Enter your age\"\n [allowOnlyDigits]=\"true\"\n [allowNegative]=\"false\"\n [min]=\"0\"\n [max]=\"120\"\n>\n</ui-field>\n```\n\n### Search field\n\n```html\n<ui-field type=\"search\" placeholder=\"Search...\"> </ui-field>\n```\n\n### Field with error messages\n\n```html\n<ui-field type=\"email\" label=\"Email\" placeholder=\"Enter your email\" [errors]=\"['Please enter a valid email address']\">\n</ui-field>\n```\n\n### Text area\n\n```html\n<ui-field type=\"textarea\" label=\"Comments\" placeholder=\"Enter your comments\" [maxCharacters]=\"500\"> </ui-field>\n```\n\n### Required field\n\n```html\n<ui-field type=\"text\" label=\"Username\" placeholder=\"Enter your username\" [required]=\"true\"> </ui-field>\n```\n","whenToUse":["**Always include a label** above the field — never rely on a placeholder as the label","**Helper text below the field** for format guidance (e.g. 'Enter the full URL including https://')","**Trigger the error state on blur or form submission** — not on every keystroke while the user is typing"],"whenNotToUse":["**Multi-line text** → use a textarea variant instead","**Structured data with a defined format** (dates, phone numbers, currency) → use the specialised input component","**Hiding the label on focus** → always keep it visible so the user knows what they're filling in","**An AI button bolted below a textarea** → AI generate/assist affordances live inside the text area; use the Write with AI component (see 'AI-assisted input' below)"],"template":"<ng-container>\n <mat-form-field\n [ngStyle]=\"{\n '--textarea-height': textareaHeight - 10 + 'px',\n '--textarea-height-container': textareaHeight + 'px',\n }\"\n #uiField\n appearance=\"outline\"\n hideRequiredMarker=\"true\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) || hasError ? 'warn' : 'accent'\"\n [ngClass]=\"{\n 'hide-bottom-content': !showBottomContent,\n textarea: type === 'textarea' || type === 'textarea-scrollable',\n 'textarea-scrollable': type === 'textarea-scrollable',\n 'multi-line': type === 'multi-line',\n 'multi-line-textarea': type === 'multi-line' && autosizableTextarea && borderless,\n 'keyboard-focused': keyboardFocused(),\n 'has-label': !!label,\n 'has-value': !!value,\n 'text-area-borderless': borderless && (type === 'textarea' || type === 'textarea-scrollable'),\n }\"\n (mouseenter)=\"onActive(true, 'hover')\"\n (mouseleave)=\"onActive(false, 'hover')\"\n >\n <span [style.display]=\"'none'\" [id]=\"ariaDescribedbyId()\">\n @if (required) {\n <span>{{ 'ERRORS.REQUIRED' | uiTranslate | async }}</span>\n }\n {{ ariaDescribedby() }}\n </span>\n <span [style.display]=\"'none'\" [id]=\"ariaLabelledbyId()\">\n {{ ariaLabelledby() ?? label }}\n </span>\n @if (\n (label || labelHtml()) &&\n ((type !== 'search' && applicationTheme === 'classic') || applicationTheme !== 'classic')\n ) {\n <mat-label class=\"label-with-icon\">\n @if (isAIVariant || labelIcon) {\n <ui-icon [name]=\"isAIVariant ? 'Sparkle-in-line' : labelIcon!\" [size]=\"'16'\" class=\"label-icon\"></ui-icon>\n }\n <span>\n @if (labelHtml()) {\n <span [innerHTML]=\"labelHtml()\"></span>\n } @else {\n {{ label }}\n }\n @if (required) {\n <span>*</span>\n }\n </span>\n </mat-label>\n }\n @if (type === 'search' || type === 'collapsed-search') {\n <mat-icon matIconPrefix class=\"search-icon\" [svgIcon]=\"'Search'\"></mat-icon>\n }\n @if (type !== 'textarea' && type !== 'textarea-scrollable' && type !== 'multi-line') {\n <input\n data-testid=\"field-input\"\n [readonly]=\"readOnly\"\n matInput\n #inputElement\n (blur)=\"onTouch(); onActive(false, 'focus'); onBlur()\"\n (input)=\"onInput($event)\"\n [id]=\"id()\"\n (keyup)=\"onChangeInputSearch()\"\n [placeholder]=\"placeholder!\"\n [value]=\"value\"\n [disabled]=\"disabled || loading\"\n [type]=\"currentType\"\n (keyup.enter)=\"onSubmit()\"\n [max]=\"max\"\n [min]=\"min\"\n [name]=\"fieldName!\"\n [required]=\"required\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-invalid]=\"errorsLength || (ngControl?.errors && ngControl?.touched) || hasError\"\n [attr.aria-labelledby]=\"ariaLabelledbyId()\"\n [attr.aria-describedby]=\"ariaDescribedbyId()\"\n role=\"textbox\"\n digitsOnly\n [autocomplete]=\"isAutocompleteOff ? 'off' : 'on'\"\n [allowOnlyDigits]=\"allowOnlyDigits\"\n [allowNegative]=\"allowNegative\"\n (focusin)=\"onActive(true, 'focus')\"\n />\n } @else {\n <textarea\n matInput\n #inputElement\n #autosize=\"cdkTextareaAutosize\"\n [readonly]=\"readOnly\"\n (blur)=\"onTouch(); onActive(false, 'focus'); onBlur()\"\n (input)=\"onInput($event)\"\n [id]=\"id()\"\n (keyup)=\"onChangeInputSearch()\"\n (keyup.enter)=\"onSubmit()\"\n [placeholder]=\"placeholder!\"\n [value]=\"value\"\n [disabled]=\"disabled\"\n [name]=\"fieldName!\"\n [required]=\"required\"\n [attr.aria-label]=\"!label ? ariaLabel : ''\"\n [attr.aria-invalid]=\"errorsLength || (ngControl?.errors && ngControl?.touched) || hasError\"\n [attr.aria-labelledby]=\"ariaLabelledbyId()\"\n [attr.aria-describedby]=\"ariaDescribedbyId()\"\n role=\"textbox\"\n [cdkTextareaAutosize]=\"type === 'multi-line'\"\n [cdkAutosizeMinRows]=\"type === 'multi-line' ? 1 : 5\"\n [cdkAutosizeMaxRows]=\"type === 'multi-line' ? maxRows : 5\"\n [attr.maxlength]=\"maxCharacters\"\n (focusin)=\"onActive(true, 'focus')\"\n ></textarea>\n }\n\n @if (type === 'search' || type === 'collapsed-search' || type === 'password' || type === 'validation-text') {\n <div class=\"options\">\n <div class=\"options-container\">\n @if (showClose) {\n <ui-button\n variant=\"secondary\"\n [justIcon]=\"true\"\n class=\"close\"\n iconName=\"Close\"\n [label]=\"('COMMON.CLEAR' | uiTranslate | async)!\"\n (buttonClickEvent)=\"clearValue()\"\n [ariaLabel]=\"ariaLabel + '--' + ('COMMON.CLEAR' | uiTranslate | async)\"\n ></ui-button>\n }\n @if (type === 'password') {\n <ui-button\n class=\"password\"\n variant=\"secondary\"\n [tooltip]=\"((showPassword ? 'FIELD.HIDE_PASSWORD' : 'FIELD.SHOW_PASSWORD') | uiTranslate | async)!\"\n [justIcon]=\"true\"\n role=\"switch\"\n [attr.aria-checked]=\"showPassword\"\n [iconName]=\"getPasswordIcon\"\n [applicationTheme]=\"applicationTheme\"\n (click)=\"showPasswordClick()\"\n #btn\n (keydown.enter)=\"refocusPasswordButton(btn)\"\n (keydown.space)=\"refocusPasswordButton(btn)\"\n ></ui-button>\n }\n @if (\n type === 'validation-text' && (applicationTheme === 'dark' || applicationTheme === 'light') && value.length\n ) {\n @if (!isValid) {\n <ui-button\n class=\"validation\"\n [variant]=\"'text'\"\n [applicationTheme]=\"'light'\"\n [label]=\"'Validate'\"\n [loading]=\"loading\"\n (click)=\"validate()\"\n [disabled]=\"disabled\"\n [size]=\"'small'\"\n ></ui-button>\n }\n @if (isValid) {\n <ui-icon class=\"valid\" [color]=\"'white'\" applicationTheme=\"light\" [name]=\"'Check'\"></ui-icon>\n }\n }\n </div>\n </div>\n }\n @if (\n hasTextAreaCounter &&\n (type === 'textarea' || type === 'textarea-scrollable') &&\n !errorsLength &&\n !(ngControl?.errors | hasValidationError)\n ) {\n <mat-hint class=\"info\">{{ value.length }} / {{ maxCharacters }}</mat-hint>\n }\n\n @if (\n hintMessage &&\n !errorsLength &&\n !(ngControl?.errors | hasValidationError) &&\n type !== 'textarea' &&\n type !== 'textarea-scrollable'\n ) {\n <mat-hint class=\"info\">{{ hintMessage }}</mat-hint>\n }\n\n @if (errorsLength || (ngControl?.errors | hasValidationError)) {\n <mat-hint class=\"error\">\n @if (errorsLength) {\n @for (error of _errors; track trackByFn($index, error)) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span [innerHTML]=\"error\"></span>\n </div>\n }\n }\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error [ngControl]=\"ngControl\" [label]=\"hideLabelInErrors ? null : label\"></ui-validation-error>\n }\n </mat-hint>\n }\n\n @if (badgeVariant) {\n <ui-badge class=\"field-badge\" [variant]=\"badgeVariant\"></ui-badge>\n }\n </mat-form-field>\n</ng-container>\n","queryGuide":{"roles":["textbox","switch"],"ariaAttributes":["aria-label","aria-invalid","aria-labelledby","aria-describedby","aria-checked"],"dataTestIds":["field-input"],"materialComponents":["mat-form-field","mat-label","mat-icon","mat-hint"],"childUIComponents":["ui-icon","ui-button","ui-validation-error","ui-badge"],"suggestedQueries":["screen.getByRole('textbox')","screen.getByRole('switch')","screen.getByLabelText('label text')","screen.getByTestId('field-input')"]},"filePath":"components/field/field.component.ts","supportsTheme":true,"lastModified":1786626756396.4314},"file-upload":{"name":"FileUploadComponent","selector":"ui-file-upload","category":"file upload","description":"A drag-and-drop or click-to-browse file input for attaching files to the product. Supports file type and size validation, upload progress feedback, and multi-file selection. Used in candidate resume upload, bulk invite CSV import, and company branding asset upload.","importPath":"@testgorilla/tgo-ui/components/file-upload","moduleName":"FileUploadComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"maxFileSizeMB","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"minFileSizeMB","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"supportedFileTypes","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Supported files for input field","isSignal":false},{"name":"uploadProgress","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Upload progress","isSignal":false},{"name":"showUploadProgress","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show Upload progress percentage or not","isSignal":false},{"name":"fileProcessingMessage","type":"string | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Custom progress message","isSignal":false},{"name":"errors","type":"string[] | null","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"File upload errors","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"File upload disabled","isSignal":false},{"name":"file","type":"File | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Existing file information","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the file upload.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"showFileSize","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Whether to show the file size information.","isSignal":false}],"outputs":[{"name":"OnDrop","eventType":"File","alias":null,"description":"","isSignal":false}],"module":{"name":"FileUploadComponentModule","imports":["CommonModule","MatFormFieldModule","MatInputModule","IconComponentModule","ProgressBarComponentModule","UiTranslatePipe","FileUploadComponent","DragDropDirective"],"exports":["FileUploadComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A drag-and-drop or click-to-browse file input for attaching files to the product. Supports file type and size validation, upload progress feedback, and multi-file selection. Used in candidate resume upload, bulk invite CSV import, and company branding asset upload.\n\n## When to use\n\n- **Accepted file types and maximum size** — specify them in the component and in helper text so users aren't left to discover limits through error messages\n- **Upload progress for larger files** — show progress for files over ~500KB so users know the upload is in flight\n- **Clear success state** — once a file is attached, show the filename alongside a remove option\n\n## When NOT to use\n\n- **Images that need cropping or preview** → pair the upload with a dedicated image editor flow instead\n- **Unlimited file size without backend validation** → always enforce limits on both client and server\n- **Collapsing the upload area after attaching** → don't hide it without showing what was uploaded\n","whenToUse":["**Accepted file types and maximum size** — specify them in the component and in helper text so users aren't left to discover limits through error messages","**Upload progress for larger files** — show progress for files over ~500KB so users know the upload is in flight","**Clear success state** — once a file is attached, show the filename alongside a remove option"],"whenNotToUse":["**Images that need cropping or preview** → pair the upload with a dedicated image editor flow instead","**Unlimited file size without backend validation** → always enforce limits on both client and server","**Collapsing the upload area after attaching** → don't hide it without showing what was uploaded"],"template":"<ng-container>\n <div\n class=\"upload-file-container\"\n uiDragDrop\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-required]=\"ariaRequired\"\n [attr.theme]=\"applicationTheme\"\n [ngClass]=\"{ 'upload-errors': !!errors || isMissedMinSize, 'upload-file-container-disabled': disabled }\"\n (fileDropped)=\"onFileDropped($event)\"\n tabindex=\"0\"\n [tabIndex]=\"0\"\n (keydown)=\"onKeydown($event)\"\n (click)=\"fileDrop?.nativeElement?.click()\"\n >\n @if (browse && !(success && !!file && progress)) {\n <div [@inOutAnimation] class=\"upload-files\">\n <div class=\"upload-browse\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\"></ng-container>\n @if (!!errors || isMissedMinSize) {\n <div class=\"errors\" role=\"alert\">\n @if (!!errors) {\n @for (error of errors; track error) {\n <span><ui-icon name=\"Error\" class=\"error-icon\"></ui-icon> {{ error }}</span>\n }\n }\n @if (isMissedMinSize) {\n <span>\n <ui-icon name=\"Error\" class=\"error-icon\"></ui-icon>\n {{ translationContext + 'MIN_SIZE_ERROR' | uiTranslate: { min: minFileSizeMB } | async }}\n </span>\n }\n </div>\n }\n <input\n data-testid=\"file-upload-input\"\n [attr.aria-labelledby]=\"success || progress ? '' : 'drag-and-drop'\"\n [attr.aria-describedby]=\"'upload-supported-files'\"\n [tabIndex]=\"-1\"\n class=\"form-control\"\n #fileDrop\n type=\"file\"\n id=\"file\"\n [accept]=\"supportedFileTypes\"\n [disabled]=\"disabled\"\n (change)=\"onChangeUpload($event)\"\n />\n @if (applicationTheme === 'classic') {\n <p>\n {{ translationContext + 'DRAG_AND_DROP_OR' | uiTranslate | async }}\n <span class=\"semibold\"> {{ translationContext + 'BROWSE' | uiTranslate | async | lowercase }}</span>\n {{ translationContext + 'YOUR_FILES' | uiTranslate | async }}\n </p>\n } @else {\n <p id=\"'drag-and-drop'\">{{ translationContext + 'DRAG_AND_DROP' | uiTranslate | async }}</p>\n }\n <span class=\"upload-text\"\n >{{ maxFileSizeMB }}{{ translationContext + 'MAX_SIZE' | uiTranslate | async }}</span\n >\n @if (minFileSizeMB) {\n <span class=\"upload-text\"\n >{{ minFileSizeMB }}{{ translationContext + 'MIN_SIZE' | uiTranslate | async }}</span\n >\n }\n </div>\n </div>\n }\n @if (success) {\n <div class=\"upload-success\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\"></ng-container>\n <span class=\"upload-success-text\"\n ><ui-icon name=\"Check-round-in-line\" [color]=\"colorUploadSuccess\"></ui-icon\n >{{ translationContext + 'FILE_UPLOADING_SUCCESS' | uiTranslate | async }}</span\n >\n <input\n data-testid=\"file-upload-success-input\"\n [tabIndex]=\"-1\"\n [attr.aria-labelledby]=\"success || progress ? '' : 'drag-and-drop'\"\n [attr.aria-describedby]=\"success || progress ? '' : 'supported-file-types'\"\n class=\"form-control\"\n #fileDrop\n type=\"file\"\n id=\"fileTest\"\n [accept]=\"supportedFileTypes\"\n (change)=\"onChangeUpload($event)\"\n [disabled]=\"disabled\"\n />\n <span class=\"upload-text\">{{ file?.name }}</span>\n @if (showFileSize) {\n <span class=\"upload-text\">{{\n translationContext + 'FILE_SIZE' | uiTranslate: { fileSize: fileSizeInMB } | async\n }}</span>\n }\n </div>\n }\n @if (!!progress && !!file && !success) {\n <div class=\"upload-progress\" [@inOutAnimationProgress]>\n <p>{{ file.name }}</p>\n <ui-progress-bar\n [progress]=\"uploadProgress\"\n [companyColor]=\"compColor\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-progress-bar>\n <p class=\"upload-text\">\n {{ fileProcessingMessage ?? (translationContext + 'UPLOADING' | uiTranslate | async) }}\n @if (showUploadProgress) {\n <span>{{ uploadProgress }}</span\n >%\n }\n </p>\n </div>\n }\n\n <ng-template #iconTpl>\n @if (applicationTheme === 'classic') {\n <ui-icon class=\"upload-icon\" name=\"File-upload\"></ui-icon>\n } @else {\n <ui-icon class=\"upload-icon\" name=\"Upload\" [size]=\"'24'\" [applicationTheme]=\"applicationTheme\"></ui-icon>\n }\n </ng-template>\n\n @if (!!supportedFileTypes) {\n <div [attr.theme]=\"applicationTheme\" class=\"upload-supported-files\" id=\"upload-supported-files\">\n <span>{{ translationContext + 'SUPPORTED_FILE_TYPES' | uiTranslate | async }}</span\n >{{ supportedFileTypes }}\n </div>\n }\n </div>\n</ng-container>\n","queryGuide":{"roles":["alert"],"ariaAttributes":["aria-label","aria-required","aria-labelledby","aria-describedby"],"dataTestIds":["file-upload-input","file-upload-success-input"],"materialComponents":[],"childUIComponents":["ui-icon","ui-progress-bar"],"suggestedQueries":["screen.getByRole('alert')","screen.getByLabelText('label text')","screen.getByTestId('file-upload-input')","screen.getByTestId('file-upload-success-input')"]},"filePath":"components/file-upload/file-upload.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=848-12769","lastModified":1786626756397.0486},"filter-button":{"name":"FilterButtonComponent","selector":"ui-filter-button","category":"forms","description":"A compact, toggleable button for applying a single filter criterion to a dataset. Available in default and selected states. Multiple Filter Buttons group together to form a filter bar above tables or lists. Used in the candidate list, test library, and assessment overview to let users narrow results by status, score range, or category.","importPath":"@testgorilla/tgo-ui/components/filter-button","moduleName":"FilterButtonComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The button label when no value is selected.","isSignal":false},{"name":"message","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Optional message to display on the overflow bottom.","isSignal":false},{"name":"value","type":"FilterValue[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"List of selected values.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the component is disabled.\nDefault: false.","isSignal":false},{"name":"allowClear","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Whether the user should be allowed to clear the values.\nDefault: true.","isSignal":false},{"name":"singleSelection","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the user should be allowed to select single or multiple values.\nDefault: false.","isSignal":false},{"name":"iconName","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The icon name to display.","isSignal":false},{"name":"iconPosition","type":"ButtonIconPosition","defaultValue":"'right'","required":false,"alias":null,"transform":null,"description":"The position of the icon.\nDefault: 'right'.","isSignal":false},{"name":"noOptionsMessage","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Optional message to display if there are no options to select available","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Whether the button should stretch to fill its container, and the dropdown panel\nfollow that width instead of the default 240px.\nThe panel never shrinks below 240px, so a container narrower than that is safe.\nDefault: false.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"options","type":"FilterButtonOption[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"List of options.","isSignal":true}],"outputs":[{"name":"changed","eventType":"FilterValue[]","alias":null,"description":"Emits the list of selected values when the selection changes.","isSignal":false},{"name":"optionChange","eventType":"OptionChangeEvent","alias":null,"description":"Emits the option change event","isSignal":false}],"module":{"name":"FilterButtonComponentModule","imports":["CommonModule","FormsModule","MatTooltipModule","OverlayModule","UiTranslatePipe","ButtonComponentModule","IconComponentModule","CheckboxComponentModule","FieldComponentModule","EllipseTextDirective","RadioButtonComponentModule","FilterButtonComponent"],"exports":["FilterButtonComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"FilterValue","value":"string | number"}],"interfaces":[{"name":"FilterButtonOption","properties":[{"name":"value","type":"FilterValue","optional":false},{"name":"label","type":"string","optional":false},{"name":"labelNumber","type":"number","optional":true},{"name":"icon","type":"IconName","optional":true},{"name":"sectionTitle","type":"string","optional":true}]},{"name":"OptionChangeEvent","properties":[{"name":"selected","type":"boolean","optional":false},{"name":"option","type":"FilterButtonOption","optional":false}]}],"enums":[]},"examples":[{"name":"FullWidth","template":"<div style=\"width: 480px\"><ui-filter-button ${argsToTemplate(mergedArgs)}></ui-filter-button></div>","props":{"label":"'Full width filter'","options":"SearchOptions","fullWidth":"true"}}],"documentation":"A compact, toggleable button for applying a single filter criterion to a dataset. Available in default and selected states. Multiple Filter Buttons group together to form a filter bar above tables or lists. Used in the candidate list, test library, and assessment overview to let users narrow results by status, score range, or category.\n\n## When to use\n\n- **Group in a horizontal row** above the dataset they control\n- **Show the selected state clearly** with the filled pink style so applied filters are immediately visible\n- **Use the More Filters CTA** at the end of the row when more filter options exist than can be displayed inline\n\n## When NOT to use\n\n- **Navigation or page-level state changes** → Filter Buttons control dataset scope only\n- **More than 5–6 visible Filter Buttons** → use a More Filters overflow instead\n- **Mutually exclusive selection** → use a Segmented Button rather than Filter Buttons\n\n## How to use\n\nTo use the Filter Button component, you need to import the `FilterButtonComponentModule` in your module:\n\n```typescript\nimport { FilterButtonComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n FilterButtonComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-filter-button\n label=\"Categories\"\n [options]=\"[\n { label: 'Option 1', value: 'op1', labelNumber: 5 },\n { label: 'Option 2', value: 'op2', labelNumber: 3 },\n { label: 'Option 3', value: 'op3', labelNumber: 12 }\n ]\"\n [singleSelection]=\"false\"\n applicationTheme=\"light\"\n (selectionChange)=\"onFilterChange($event)\"\n>\n</ui-filter-button>\n```\n\nThe filter button component provides an interactive dropdown for selecting filter options, with support for single or multiple selection.\n\n## Full width\n\nBy default the button sizes to its label and the dropdown panel is a fixed 240px. Set `fullWidth` to make the\nbutton fill its container and have the panel follow that width — useful for stacked filters on narrow screens:\n\n```html\n<ui-filter-button label=\"Categories\" [options]=\"options\" fullWidth></ui-filter-button>\n```\n\nThe panel never shrinks below 240px, so a container narrower than that keeps a readable dropdown.\n","whenToUse":["**Group in a horizontal row** above the dataset they control","**Show the selected state clearly** with the filled pink style so applied filters are immediately visible","**Use the More Filters CTA** at the end of the row when more filter options exist than can be displayed inline"],"whenNotToUse":["**Navigation or page-level state changes** → Filter Buttons control dataset scope only","**More than 5–6 visible Filter Buttons** → use a More Filters overflow instead","**Mutually exclusive selection** → use a Segmented Button rather than Filter Buttons"],"template":"<ng-container>\n <ui-button\n [matTooltip]=\"tooltip()\"\n [matTooltipClass]=\"'tooltip-multi-line ' + applicationTheme\"\n [matTooltipPosition]=\"'above'\"\n class=\"ui-filter-button-trigger\"\n [class.button-opened]=\"isOpen()\"\n [variant]=\"value.length || isOpen() ? 'secondary' : 'ghost'\"\n [label]=\"displayedLabel() || label\"\n [disabled]=\"disabled || (options().length === 0 && !noOptionsMessage)\"\n (click)=\"toggle()\"\n type=\"button\"\n size=\"small\"\n [role]=\"'combobox'\"\n [tooltip]=\"value.length ? ' ' : ''\"\n [attr.aria-expanded]=\"isOpen()\"\n [iconName]=\"iconName || (isOpen() ? 'Arrow-chevron-up-filled' : 'Arrow-chevron-down-filled')\"\n [iconPosition]=\"iconPosition\"\n [applicationTheme]=\"applicationTheme\"\n [fullWidth]=\"fullWidth\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n ></ui-button>\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"fullWidth\"\n [cdkConnectedOverlayMinWidth]=\"fullWidth ? PANEL_MIN_WIDTH : ''\"\n [cdkConnectedOverlayPanelClass]=\"fullWidth ? MATCH_TRIGGER_PANEL_CLASS : ''\"\n (overlayOutsideClick)=\"outsideClick()\"\n >\n @if (activeOption(); as active) {\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n translationContext + 'ACTIVE_ITEM' | uiTranslate: { active: activePosition(), total: optionCount() } | async\n }}\n </div>\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n (value.includes(active.value) ? translationContext + 'SELECTED' : translationContext + 'DESELECTED')\n | uiTranslate\n | async\n }}\n </div>\n }\n <div class=\"ui-filter-button\" #overlayContainer>\n @if (options().length > MIN_OPTIONS_LENGTH_FOR_SEARCH) {\n <div class=\"ui-filter-button-header\">\n <ui-field\n type=\"search\"\n [showBottomContent]=\"false\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"onSearchChange($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-field>\n </div>\n }\n <div\n class=\"ui-filter-button-list\"\n #list\n tabindex=\"0\"\n role=\"listbox\"\n [attr.aria-activedescendant]=\"isOpen() && activeOption() ? 'ui-fb-option-' + activeIndex() : null\"\n (keydown)=\"keyDownHandler($event)\"\n >\n @if (!displayedOptions().length && options().length) {\n <div class=\"no-results-found\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </div>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"ui-filter-button-viewport\"\n [itemSize]=\"ITEM_SIZE\"\n [style.height.px]=\"listHeight()\"\n >\n <ng-container *cdkVirtualFor=\"let row of flatOptions(); let i = index; trackBy: trackByRow\">\n @if (row.kind === 'header') {\n <div class=\"section-title\">\n {{ row.title }}\n </div>\n } @else {\n <div\n class=\"item\"\n role=\"option\"\n tabindex=\"-1\"\n [id]=\"'ui-fb-option-' + i\"\n [class.item-selected]=\"isSelected(row.option.value)\"\n [class.item-active]=\"activeIndex() === i\"\n [attr.aria-selected]=\"isSelected(row.option.value)\"\n (click)=\"activeIndex.set(i); optionChanged(!isSelected(row.option.value), row.option)\"\n (keydown.enter)=\"optionChanged(!isSelected(row.option.value), row.option)\"\n >\n @if (singleSelection) {\n <ui-radio-button\n (click)=\"$event.stopPropagation()\"\n [tabIndex]=\"-1\"\n [selected]=\"isSelected(row.option.value)\"\n (changeRadio)=\"optionChanged($event.optionChecked, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n >\n </ui-radio-button>\n }\n @if (!singleSelection) {\n <ui-checkbox\n (click)=\"$event.stopPropagation()\"\n [class.with-label-number]=\"row.option.labelNumber !== undefined\"\n [tabIndex]=\"-1\"\n [checked]=\"isSelected(row.option.value)\"\n (changed)=\"optionChanged($event, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-checkbox>\n }\n @if (row.option.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"row.option.icon\"></ui-icon>\n }\n <span\n #tooltip=\"matTooltip\"\n class=\"item-label\"\n uiEllipseText\n [refresh]=\"row.option.value\"\n (onChangeTextState)=\"onLabelEllipsisChange($event, row.option.value)\"\n [matTooltip]=\"showTooltip[row.option.value] ? row.option.label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ row.option.label }}\n </span>\n @if (row.option.labelNumber !== undefined) {\n <span class=\"item-number\">({{ row.option.labelNumber }})</span>\n }\n </div>\n }\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </div>\n @if (!options().length && !!noOptionsMessage) {\n <div class=\"ui-filter-button-no-options\" [innerHTML]=\"noOptionsMessage\"></div>\n } @else {\n @if (allowClear || message) {\n <div class=\"ui-filter-button-footer\">\n @if (allowClear) {\n <ui-button\n variant=\"text\"\n size=\"small\"\n [label]=\"('COMMON.RESET' | uiTranslate | async)!\"\n (click)=\"clearValue()\"\n [applicationTheme]=\"applicationTheme\"\n (keydown.escape)=\"toggle()\"\n ></ui-button>\n }\n <div class=\"ui-filter-button-footer-message\">\n {{ message }}\n </div>\n </div>\n }\n }\n </div>\n </ng-template>\n</ng-container>\n","queryGuide":{"roles":["listbox","option"],"ariaAttributes":["aria-expanded","aria-live","aria-activedescendant","aria-selected"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-button","ui-field","ui-radio-button","ui-checkbox","ui-icon"],"suggestedQueries":["screen.getByRole('listbox')","screen.getByRole('option')"]},"filePath":"components/filter-button/filter-button.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=14712-22549","lastModified":1786626756397.488},"gaussian-chart":{"name":"GaussianChartComponent","selector":"ui-gaussian-chart","category":"charts","description":"A bell-curve chart that shows a candidate's score relative to the normal distribution of all candidates who have taken the same test. Displays the candidate's percentile band and compares them to the highest scorer in a specific assessment. Used exclusively in candidate score interpretation within test results.","importPath":"@testgorilla/tgo-ui/components/gaussian-chart","moduleName":"GaussianChartComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"score","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The candidate score to be displayed on the chart.","isSignal":false},{"name":"bestCandidateScore","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The best candidate score to be displayed on the chart.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"legendPosition","type":"LegendPosition","defaultValue":"'bottom-start'","required":false,"alias":null,"transform":null,"description":"Controls the position of the legend relative to the chart.","isSignal":false}],"outputs":[],"module":{"name":"GaussianChartComponentModule","imports":["NgClass","UiTranslatePipe","BaseChartDirective","MatTooltipModule","CommonModule","IconComponentModule","MemoizeFuncPipe","GaussianChartComponent"],"exports":["GaussianChartComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A bell-curve chart that shows a candidate's score relative to the normal distribution of all candidates who have taken the same test. Displays the candidate's percentile band and compares them to the highest scorer in a specific assessment. Used exclusively in candidate score interpretation within test results.\n\n## When to use\n\n- **Normative frame of reference** — give hiring managers context the raw score alone can't, since it's less meaningful without the distribution\n- **Both markers visible** — always show the candidate's score marker and the highest-in-assessment marker so the comparison is immediate\n- **Legend placement** — use the Legend Top Start or End variant to position the legend where it doesn't overlap the curve\n\n## When NOT to use\n\n- **Implying false precision** → don't use it to suggest accuracy beyond what the test norm supports; it's a relative performance visualisation, not an absolute measurement\n- **Small norm populations** → don't show it for tests with very small norms (fewer than ~100 candidates)\n- **Band labels** → don't remove or reposition Well below / Below / Average / Above / Well above; they're calibrated to the norm percentiles\n","whenToUse":["**Normative frame of reference** — give hiring managers context the raw score alone can't, since it's less meaningful without the distribution","**Both markers visible** — always show the candidate's score marker and the highest-in-assessment marker so the comparison is immediate","**Legend placement** — use the Legend Top Start or End variant to position the legend where it doesn't overlap the curve"],"whenNotToUse":["**Implying false precision** → don't use it to suggest accuracy beyond what the test norm supports; it's a relative performance visualisation, not an absolute measurement","**Small norm populations** → don't show it for tests with very small norms (fewer than ~100 candidates)","**Band labels** → don't remove or reposition Well below / Below / Average / Above / Well above; they're calibrated to the norm percentiles"],"template":"@if (score) {\n <div\n class=\"chart-section\"\n [attr.aria-label]=\"\n translationContext + 'ARIA_LABEL'\n | uiTranslate\n : {\n score: score,\n activeBucket: getScoreTranslationKey(score) | uiTranslate | async,\n bestCandidateScore: bestCandidateScore,\n activeBucketBestScore: getScoreTranslationKey(bestCandidateScore) | uiTranslate | async,\n }\n | async\n \"\n tabindex=\"0\"\n >\n <!-- Legend positioned at top -->\n @if (legendPosition === 'top-start' || legendPosition === 'top-end') {\n <ng-container [ngTemplateOutlet]=\"legendTemplate\"></ng-container>\n }\n <div class=\"chart-range-wrapper\">\n <div class=\"normal-chart-container\">\n <div class=\"normal-chart\">\n <canvas baseChart [data]=\"gaussianChartData\" [options]=\"gaussianChartOptions\" type=\"line\" class=\"chart\">\n </canvas>\n <div\n class=\"overlay-icon\"\n [style.bottom]=\"[overlayYValue, 3, 'subtract'] | memoizeFunc: getCalcPosition\"\n [style.left]=\"[overlayXValue, 20, 'subtract'] | memoizeFunc: getCalcPosition\"\n >\n <div class=\"drop-icon\">\n <ui-icon name=\"Pin-marker\" size=\"40\"></ui-icon>\n <span class=\"curve-score\">{{ score }}</span>\n </div>\n </div>\n <div\n class=\"best-score\"\n [style.height]=\"\n [100 - overlayYBestScoreValue, normalContainerPadding, 'add'] | memoizeFunc: getCalcPosition\n \"\n [style.bottom]=\"overlayYBestScoreValue + '%'\"\n [style.left]=\"overlayXBestScoreValue + '%'\"\n >\n <div\n class=\"best-score-content\"\n [style.left]=\"bestCandidateScore | memoizeFunc: getBestScoreContentPosition : this\"\n >\n <ui-icon name=\"Trophy\"></ui-icon>\n {{ bestCandidateScore }}\n </div>\n </div>\n </div>\n <div class=\"overlay\">\n <div class=\"overlay-column\" [class.active]=\"activeBucket === PercentileThreshold.LOWEST\">\n <div\n class=\"column-item\"\n [class.text-bold]=\"activeBucket === PercentileThreshold.LOWEST\"\n [matTooltip]=\"\n (translationContext + 'PERCENTAGE_BUCKETS_GRAPH.LOWEST_TOOLTIP' | uiTranslate | async) || ''\n \"\n >\n {{ translationContext + 'PERCENTAGE_BUCKETS_GRAPH.LOWEST' | uiTranslate | async }}\n </div>\n </div>\n <div class=\"overlay-column\" [class.active]=\"activeBucket === PercentileThreshold.LOWER\">\n <div\n class=\"column-item\"\n [class.text-bold]=\"activeBucket === PercentileThreshold.LOWER\"\n [matTooltip]=\"(translationContext + 'PERCENTAGE_BUCKETS_GRAPH.LOWER_TOOLTIP' | uiTranslate | async) || ''\"\n >\n {{ translationContext + 'PERCENTAGE_BUCKETS_GRAPH.LOWER' | uiTranslate | async }}\n </div>\n </div>\n <div class=\"overlay-column\" [class.active]=\"activeBucket === PercentileThreshold.AVERAGE\">\n <div\n class=\"column-item\"\n [class.text-bold]=\"activeBucket === PercentileThreshold.AVERAGE\"\n [matTooltip]=\"\n (translationContext + 'PERCENTAGE_BUCKETS_GRAPH.AVERAGE_TOOLTIP' | uiTranslate | async) || ''\n \"\n >\n {{ translationContext + 'PERCENTAGE_BUCKETS_GRAPH.AVERAGE' | uiTranslate | async }}\n </div>\n </div>\n <div class=\"overlay-column\" [class.active]=\"activeBucket === PercentileThreshold.HIGHER\">\n <div\n class=\"column-item\"\n [class.text-bold]=\"activeBucket === PercentileThreshold.HIGHER\"\n [matTooltip]=\"\n (translationContext + 'PERCENTAGE_BUCKETS_GRAPH.HIGHER_TOOLTIP' | uiTranslate | async) || ''\n \"\n >\n {{ translationContext + 'PERCENTAGE_BUCKETS_GRAPH.HIGHER' | uiTranslate | async }}\n </div>\n </div>\n <div class=\"overlay-column\" [class.active]=\"activeBucket === PercentileThreshold.HIGHEST\">\n <div\n class=\"column-item\"\n [class.text-bold]=\"activeBucket === PercentileThreshold.HIGHEST\"\n [matTooltip]=\"\n (translationContext + 'PERCENTAGE_BUCKETS_GRAPH.HIGHEST_TOOLTIP' | uiTranslate | async) || ''\n \"\n >\n {{ translationContext + 'PERCENTAGE_BUCKETS_GRAPH.HIGHEST' | uiTranslate | async }}\n </div>\n </div>\n </div>\n </div>\n <div class=\"range\">\n <div>1</div>\n <div>20</div>\n <div>40</div>\n <div>60</div>\n <div>80</div>\n <div>99</div>\n </div>\n </div>\n <!-- Legend positioned at bottom -->\n @if (legendPosition === 'bottom-start' || legendPosition === 'bottom-end') {\n <ng-container [ngTemplateOutlet]=\"legendTemplate\"></ng-container>\n }\n </div>\n}\n\n<!-- Reusable legend template -->\n<ng-template #legendTemplate>\n <div class=\"legend-container\" [ngClass]=\"'legend-' + legendPosition\">\n <div class=\"legend\">\n <ui-icon name=\"Dot\" color=\"gray\"></ui-icon>\n {{ translationContext + 'CANDIDATE_SCORE' | uiTranslate | async }}\n </div>\n <div class=\"legend\">\n <ui-icon name=\"Trophy\" color=\"gray\"></ui-icon>\n {{ translationContext + 'HIGHEST_SCORE' | uiTranslate | async }}\n </div>\n </div>\n</ng-template>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByLabelText('label text')"]},"filePath":"components/gaussian-chart/gaussian-chart.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=13198-19286","lastModified":1786626756397.9917},"icon":{"name":"IconComponent","selector":"ui-icon","category":"icons","description":"Renders any icon from the Canopy-UI icon library at the correct size and colour. IMPORTANT: the `size` input accepts ONLY the string values '16', '24', '40', or '80' — any other value is applied unclamped and the glyph renders at an uncontrolled (typically huge) size. Supports outline (default) and filled variants, inline and standard sizing, and CSS variable-based colour overrides. Icons are always 24x24 in inline mode. Use the `--cnp-icon-color` CSS custom property on a wrapper element to theme icons consistently without prop-drilling.","importPath":"@testgorilla/tgo-ui/components/icon","moduleName":"IconComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"IconSize","defaultValue":"'16'","required":false,"alias":null,"transform":null,"description":"Icon size. Accepts ONLY '16' | '24' | '40' | '80' (pixel sizes, as strings).\nThere is no runtime validation or clamping — any other value renders the\nglyph at an uncontrolled (typically huge) size. Pick the nearest supported\nvalue instead of passing a custom one.","isSignal":false},{"name":"cssClass","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Icon css class","isSignal":false},{"name":"name","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Icon name","isSignal":false},{"name":"color","type":"IconColor","defaultValue":"'black'","required":false,"alias":null,"transform":null,"description":"Icon color","isSignal":false},{"name":"filled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Icon filled","isSignal":false},{"name":"toggleIconStyle","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"useFullIconName","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[],"module":{"name":"IconComponentModule","imports":["CommonModule","MatIconModule","IconComponent"],"exports":["IconComponent"],"declarations":[],"providers":["provideHttpClient(withInterceptorsFromDi())"]},"types":{"typeAliases":[{"name":"TgoIcons","value":"(typeof tgoIcons)[number]"},{"name":"TgoRebrandIcons","value":"(typeof tgoRebrandIcons)[number]"},{"name":"IconSize","value":"'16' | '24' | '40' | '80'"},{"name":"IconName","value":"TgoRebrandIcons | TgoIcons | ''"},{"name":"IconColor","value":"string"}],"interfaces":[],"enums":[]},"examples":[{"name":"IconColors","template":"<div class=\"row\"><div class=\"colum\">Black<div class=\"icon-container\"><ui-icon name=\"Promotion\" size=\"24\"></ui-icon></div></div>\n <div class=\"colum\">Teal<div class=\"icon-container\"><ui-icon name=\"Promotion\" size=\"24\" color=\"teal\"></ui-icon></div></div>\n <div class=\"colum\">Petrol<div class=\"icon-container\"><ui-icon name=\"Promotion\" size=\"24\" color=\"petrol\"></ui-icon></div></div>\n <div class=\"colum\">Dark<div class=\"icon-container\"><ui-icon name=\"Promotion\" size=\"24\" color=\"dark\"></ui-icon></div></div>\n <div class=\"colum\">White<div class=\"icon-container white\"><ui-icon name=\"Promotion\" size=\"24\" color=\"white\"></ui-icon></div></div></div>","props":{}},{"name":"IconCnpColorOnHost","template":"<div class=\"cnp-demo\">\n <div class=\"cnp-demo__grid\">\n <div class=\"cnp-demo__cell\">\n <code class=\"cnp-demo__caption\" [innerText]=\"captionBrandClass\"></code>\n <div class=\"cnp-demo__frame\">\n <ui-icon name=\"Archive\" size=\"40\" class=\"cnp-color-brand\"></ui-icon>\n </div>\n <span class=\"cnp-demo__note\">CSS variable set on the icon via a class that interpolates a lib token.</span>\n </div>\n\n <div class=\"cnp-demo__cell\">\n <code class=\"cnp-demo__caption\">color=\"gray\" + class=\"cnp-color-brand\"</code>\n <div class=\"cnp-demo__frame\">\n <ui-icon name=\"Archive\" size=\"40\" color=\"gray\" class=\"cnp-color-brand\"></ui-icon>\n </div>\n <span class=\"cnp-demo__note\">CSS variable wins over the legacy [color] input.</span>\n </div>\n\n <div class=\"cnp-demo__cell\">\n <code class=\"cnp-demo__caption\">color=\"gray\"</code>\n <div class=\"cnp-demo__frame\">\n <ui-icon name=\"Archive\" size=\"40\" color=\"gray\"></ui-icon>\n </div>\n <span class=\"cnp-demo__note\">No CSS variable — legacy [color] still applies.</span>\n </div>\n </div>\n </div>","props":{}},{"name":"IconCnpColorOnAncestor","template":"<div class=\"cnp-demo\">\n <div class=\"cnp-demo__scope\">\n <code class=\"cnp-demo__scope-hint\" [innerText]=\"captionScopeRule\"></code>\n <div class=\"cnp-demo__scope-icons\">\n <div class=\"cnp-demo__scope-cell\">\n <ui-icon name=\"Search\" size=\"40\"></ui-icon>\n <span class=\"cnp-demo__note\">no [color]</span>\n </div>\n <div class=\"cnp-demo__scope-cell\">\n <ui-icon name=\"Archive\" size=\"40\" color=\"gray\"></ui-icon>\n <span class=\"cnp-demo__note\">color=\"gray\"</span>\n </div>\n <div class=\"cnp-demo__scope-cell\">\n <ui-icon name=\"Team\" size=\"40\" color=\"rebrand-black\"></ui-icon>\n <span class=\"cnp-demo__note\">color=\"rebrand-black\"</span>\n </div>\n </div>\n </div>\n </div>","props":{}}],"documentation":"Renders any icon from the Canopy-UI icon library at the correct size and colour. IMPORTANT: the `size` input accepts ONLY the string values '16', '24', '40', or '80' — any other value is applied unclamped and the glyph renders at an uncontrolled (typically huge) size. Supports outline (default) and filled variants, inline and standard sizing, and CSS variable-based colour overrides. Icons are always 24x24 in inline mode. Use the `--cnp-icon-color` CSS custom property on a wrapper element to theme icons consistently without prop-drilling.\n\n## When to use\n\n- **`--cnp-icon-color` on a wrapper** (button, field, alert) → colours all nested icons at once and cascades automatically\n- **Outline by default** (Filled=False) for most UI contexts; switch to filled icons for active, selected, or emphasis states\n- **Pair an Icon Button with a Tooltip** → icons without labels are inaccessible\n\n## When NOT to use\n\n- **Hardcoding colours via `[color]` inside a themed component** → use `--cnp-icon-color` on the wrapper instead\n- **Icons as the only signal for status or action** → always pair with a label or tooltip for accessibility\n- **Resizing icons manually** → use only the supported `size` values ('16' | '24' | '40' | '80'); arbitrary values are not clamped, break optical weight, and render the glyph at an uncontrolled size\n\n## How to use\n\nImport `IconComponentModule` in your module:\n\n```typescript\nimport { IconComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n IconComponentModule,\n ],\n})\nexport class YourModule {}\n```\n\nThen use the component in your templates:\n\n```html\n<ui-icon name=\"Search-in-line\" size=\"24\"></ui-icon>\n```\n\n## Sizing\n\nThe `size` input accepts **only** `'16' | '24' | '40' | '80'` (pixel sizes, passed as strings). There is no runtime validation or clamping: any other value (`size=\"32\"`, `size=\"20\"`, …) is written straight into the host's `--icon-size` and has no matching glyph size class, so the SVG renders at an uncontrolled — typically huge — size. If a design calls for an in-between size, pick the nearest supported value instead of passing a custom one.\n\n## Public CSS custom property: `--cnp-icon-color`\n\nWhen set on a `ui-icon` host or any ancestor, overrides the icon's `color`. Takes priority over the legacy `[color]` input and over every named color class. Accepts any valid `<color>` value.\n\nMost TestGorilla icons paint via `fill=\"currentColor\"`, so overriding `color` cascades through to `fill` automatically. Brand icons with hardcoded fills (e.g. Microsoft, LinkedIn) intentionally keep their hardcoded colors — `--cnp-icon-color` does not affect them, matching the existing behavior of the `[color]` input. `color=\"ai\"` is the same: the AI gradient is a brand mark and is never repainted by `--cnp-icon-color`.\n\nThe headline use case is to drop the per-component \"icon color\" input. A wrapping component (button, field, alert, etc.) can set the variable on its host once, and any nested `ui-icon` picks it up via the cascade — no input forwarding required.\n\n```scss\n@use 'variables' as *;\n\n.my-button {\n --cnp-icon-color: #{$brand-50};\n}\n```\n\n```html\n<button class=\"my-button\">\n <ui-icon name=\"Search-in-line\" size=\"24\"></ui-icon>\n</button>\n```\n\nPlain CSS with any valid color value also works:\n\n```css\n.my-button {\n --cnp-icon-color: #d410aa;\n}\n```\n\nSet on a single icon directly via a class:\n\n```html\n<ui-icon name=\"Archive\" size=\"24\" class=\"my-icon-color\"></ui-icon>\n```\n\n## Migration\n\nWrapping components no longer need their own icon-color input. Replace the input + binding with a single CSS rule on the wrapper's host.\n\nBefore:\n\n```typescript\n@Component({\n selector: 'my-button',\n template: `<ui-icon [name]=\"icon\" [color]=\"iconColor\"></ui-icon>`,\n})\nexport class MyButtonComponent {\n @Input() iconColor: 'black' | 'white' | 'teal' = 'black';\n}\n```\n\nAfter:\n\n```typescript\n@Component({\n selector: 'my-button',\n template: `<ui-icon [name]=\"icon\"></ui-icon>`,\n})\nexport class MyButtonComponent {}\n```\n\n```scss\n:host {\n --cnp-icon-color: #{$brand-50}; // or any context-driven value\n}\n```\n\nNotes for testing: the override is a CSS custom property, not a class or attribute on the icon. Unit tests that previously asserted on `mat-icon`'s class list will need to inspect computed style instead (or assert the variable on the wrapper). The legacy `[color]` input still works unchanged for tests and consumers that prefer it.\n","whenToUse":["**`--cnp-icon-color` on a wrapper** (button, field, alert) → colours all nested icons at once and cascades automatically","**Outline by default** (Filled=False) for most UI contexts; switch to filled icons for active, selected, or emphasis states","**Pair an Icon Button with a Tooltip** → icons without labels are inaccessible"],"whenNotToUse":["**Hardcoding colours via `[color]` inside a themed component** → use `--cnp-icon-color` on the wrapper instead","**Icons as the only signal for status or action** → always pair with a label or tooltip for accessibility","**Resizing icons manually** → use only the supported `size` values ('16' | '24' | '40' | '80'); arbitrary values are not clamped, break optical weight, and render the glyph at an uncontrolled size"],"template":"@if (isIconLoaded) {\n <mat-icon [svgIcon]=\"_name\" [attr.class]=\"customCssClass\" [ngStyle]=\"cssStyle\"></mat-icon>\n} @else {\n <div class=\"empty-icon-placeholder\"></div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-icon"],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/icon/icon.component.ts","supportsTheme":true,"lastModified":1786626756401.2727},"icon-label":{"name":"IconLabelComponent","selector":"ui-icon-label","category":"icons","description":"A pre-composed pairing of an Icon and a text label, used for labelled icon links, navigation items, and action labels in toolbars or menus. Ensures consistent spacing and alignment between icon and label without manual layout work.","importPath":"@testgorilla/tgo-ui/components/icon-label","moduleName":"IconLabelComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"iconSize","type":"IconSize","defaultValue":"'16'","required":false,"alias":null,"transform":null,"description":"Icon size","isSignal":false},{"name":"iconName","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Icon name","isSignal":false},{"name":"text","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Label","isSignal":false},{"name":"iconColor","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Icon color","isSignal":false},{"name":"iconIndent","type":"unknown","defaultValue":"'8'","required":false,"alias":null,"transform":null,"description":"Indent between icon and label","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"IconLabelComponentModule","imports":["CommonModule","IconComponentModule","IconLabelComponent"],"exports":["IconLabelComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A pre-composed pairing of an Icon and a text label, used for labelled icon links, navigation items, and action labels in toolbars or menus. Ensures consistent spacing and alignment between icon and label without manual layout work.\n\n## When to use\n\n- **Pairing an icon with text** — use Icon + Label instead of manually placing a ui-icon next to text, as it handles spacing, alignment, and colour inheritance correctly\n- **Choosing icon placement** — leading icons for categories/navigation, trailing icons for directional cues (e.g. external link arrow)\n- **Matching the label style** — align the label to the surrounding typographic context: body-small for dense menus, label for standalone CTAs\n\n## When NOT to use\n\n- **As a button** → wrap it in a Button component with the appropriate variant instead\n- **Changing the icon-to-label gap manually** → the component spacing is calibrated to the type scale\n- **Decorative icon placement where no label is needed** → use Icon alone\n","whenToUse":["**Pairing an icon with text** — use Icon + Label instead of manually placing a ui-icon next to text, as it handles spacing, alignment, and colour inheritance correctly","**Choosing icon placement** — leading icons for categories/navigation, trailing icons for directional cues (e.g. external link arrow)","**Matching the label style** — align the label to the surrounding typographic context: body-small for dense menus, label for standalone CTAs"],"whenNotToUse":["**As a button** → wrap it in a Button component with the appropriate variant instead","**Changing the icon-to-label gap manually** → the component spacing is calibrated to the type scale","**Decorative icon placement where no label is needed** → use Icon alone"],"template":"<ui-icon [name]=\"iconName\" [size]=\"iconSize\" [color]=\"iconColor\" aria-hidden=\"true\"></ui-icon>\n<span>{{ text }}</span>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-hidden"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":[]},"filePath":"components/icon-label/icon-label.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=23491-145869","lastModified":1786626756398.4507},"infinite-scroll-table":{"name":"InfiniteScrollTableComponent","selector":"ui-infinite-scroll-table","category":"infinite-scroll-table","description":"A generic, virtualized table for large, server-paged datasets. It owns rendering, fixed-height","importPath":"@testgorilla/tgo-ui/components/infinite-scroll-table","moduleName":"InfiniteScrollTableComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"data","type":"readonly T[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Rows loaded so far. **Must change by reference** (`[...rows, ...page]`): a signal input cannot observe\n`rows.push(...)`, so an in-place append renders nothing and leaves `loadMore` disarmed.","isSignal":true},{"name":"columns","type":"InfiniteScrollTableColumn<T>[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"rowHeight","type":"number","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Fixed row height in px (the virtualization `itemSize`). Variable row heights are unsupported.","isSignal":true},{"name":"trackBy","type":"TrackByFunction<T>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Stable row identity so recycled rows re-render correctly. Takes precedence over `rowIdKey`.","isSignal":true},{"name":"rowIdKey","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Key on each row holding a stable id. Used when `trackBy` is not provided.","isSignal":true},{"name":"minBufferPx","type":"number | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"CDK's rendered buffers. Both default to a multiple of `rowHeight` (~5 and ~10 rows) rather than a\nfixed px value, which would mean a different row count at every `rowHeight`.","isSignal":true},{"name":"maxBufferPx","type":"number | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxHeight","type":"string","defaultValue":"'70vh'","required":false,"alias":null,"transform":null,"description":"Max height of the internal scroll region (e.g. `'70vh'`). The body shrinks to fit fewer rows.","isSignal":true},{"name":"fillHeight","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Stretch to the parent's height instead of the content's; `maxHeight` becomes a cap, not the target.","isSignal":true},{"name":"isCondensed","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"selectable","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Prepends a left-pinned checkbox column driven by each row's `selected` property. Consumer-managed\n(like `ui-table`): toggling only emits `selectionChange`.","isSignal":true},{"name":"rowsClickable","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Makes rows focusable and activatable by click or Enter/Space. Off by default: a read-only table\nmust not create a tab stop per rendered row or imply interactivity it does not have.","isSignal":true},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"First-load state: renders a full body of skeleton rows.","isSignal":true},{"name":"loadingMore","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Appends skeleton rows below the existing rows while the next page is fetched.","isSignal":true},{"name":"hasMore","type":"boolean, unknown","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Required, not defaulted: a silent \"never load\" default would disable the headline behaviour.","isSignal":true},{"name":"skeletonRowCount","type":"number","defaultValue":"DEFAULT_FIRST_LOAD_SKELETON_COUNT","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"loadMoreSkeletonCount","type":"number","defaultValue":"DEFAULT_LOAD_MORE_SKELETON_COUNT","required":false,"alias":null,"transform":null,"description":"Number of skeleton rows appended below the loaded rows while `loadingMore` is true.","isSignal":true},{"name":"loadingColumnCount","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Skeleton placeholder columns for a lazily-loaded column set. They deliberately sit OUTSIDE `columns`,\nso they cannot change which column is last and unpin a sticky last column.","isSignal":true},{"name":"loadMoreThreshold","type":"number","defaultValue":"DEFAULT_LOAD_MORE_THRESHOLD","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"noDataTemplate","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Shown when `data` is empty and not loading.","isSignal":true},{"name":"errorTemplate","type":"TemplateRef<InfiniteScrollTableErrorContext>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Shown when `error` is `true`. Receives `{ $implicit: retry, retry }` as context.","isSignal":true},{"name":"error","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"endOfListTemplate","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Shown once `hasMore` goes `false` with rows loaded. Deliberately has NO built-in fallback (unlike\n`noDataTemplate` / `errorTemplate`): a single-page list would otherwise flash \"no more results\".","isSignal":true}],"outputs":[{"name":"loadMore","eventType":"void","alias":null,"description":"Guarded: only when `hasMore && !loading && !loadingMore`, and only once per approach to the end.","isSignal":true},{"name":"retry","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"rowClick","eventType":"InfiniteScrollTableRowClick<T>","alias":null,"description":"","isSignal":true},{"name":"selectionChange","eventType":"InfiniteScrollTableRowSelection<T>","alias":null,"description":"The consumer is expected to apply `selected` back onto its own data.","isSignal":true}],"module":{"name":"InfiniteScrollTableComponentModule","imports":["CommonModule","ScrollingModule","InfiniteScrollTableComponent"],"exports":["InfiniteScrollTableComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"InfiniteScrollTableRowLike","value":"Record<string, unknown> & { selected?: boolean }"},{"name":"InfiniteScrollTableColumnAlignment","value":"'left' | 'right' | 'center'"}],"interfaces":[{"name":"InfiniteScrollTableColumn","properties":[{"name":"key","type":"string","optional":false},{"name":"title","type":"string","optional":true},{"name":"headerCellTemplate","type":"TemplateRef<InfiniteScrollTableHeaderCellContext<T>>","optional":true},{"name":"rowCellTemplate","type":"TemplateRef<InfiniteScrollTableRowCellContext<T>>","optional":true},{"name":"isSticky","type":"boolean","optional":true},{"name":"styles","type":"InfiniteScrollTableColumnStyles","optional":true}]},{"name":"InfiniteScrollTableColumnStyles","properties":[{"name":"width","type":"string","optional":true},{"name":"alignment","type":"InfiniteScrollTableColumnAlignment","optional":true}]},{"name":"InfiniteScrollTableRowCellContext","properties":[{"name":"$implicit","type":"T","optional":false},{"name":"rowIndex","type":"number","optional":false}]},{"name":"InfiniteScrollTableHeaderCellContext","properties":[{"name":"$implicit","type":"string | undefined","optional":false},{"name":"column","type":"InfiniteScrollTableColumn<T>","optional":false},{"name":"colIndex","type":"number","optional":false}]},{"name":"InfiniteScrollTableErrorContext","properties":[{"name":"$implicit","type":"() => void","optional":false},{"name":"retry","type":"() => void","optional":false}]},{"name":"InfiniteScrollTableRowClick","properties":[{"name":"row","type":"T","optional":false},{"name":"index","type":"number","optional":false}]},{"name":"InfiniteScrollTableRowSelection","properties":[{"name":"row","type":"T","optional":false},{"name":"index","type":"number","optional":false},{"name":"selected","type":"boolean","optional":false}]}],"enums":[]},"examples":[],"documentation":"A generic, virtualized table for large, server-paged datasets. It owns rendering, fixed-height\nvertical virtualization (Angular CDK), horizontal scroll, a pinned header, sticky first/last\ncolumns, first-load and load-more skeletons, and a guarded `loadMore` event. All content and\nlayout come from consumer inputs — nothing domain-specific lives in the component.\n\nUse it when you need scroll-driven infinite loading and virtualization for thousands of rows. For\npaginated or small datasets, use `ui-table`.\n\nIt is **not** a drop-in replacement for `ui-table`: there is no sorting, no column `hidden` / `order`,\nand no built-in cell tooltips. Column configs and cell templates need adapting, not just a selector\nswap.\n\n## Cell templates\n\n- Row cell template context: `{ $implicit: row, rowIndex }` — typed as `InfiniteScrollTableRowCellContext<T>`\n- Header cell template context: `{ $implicit: column.title, column, colIndex }` — typed as\n `InfiniteScrollTableHeaderCellContext<T>`\n\n## Key inputs\n\n- `data` — **required.** The rows loaded so far. **Must be a new array reference on every change:**\n append with `[...rows, ...page]`. A signal input cannot observe `rows.push(...)`, so an in-place\n append renders nothing and leaves `loadMore` disarmed; a dev-mode warning catches the common case (a\n page landing as `loadingMore` clears).\n- `columns` — **required.** Column defs. `isSticky` is honoured only on the **first** column (pins\n left) and the **last** column (pins right); anywhere else it is ignored with a dev-mode warning.\n- `rowHeight` — **required.** Fixed row height in px, driving virtualization. Variable and multi-line\n row heights are unsupported.\n- `hasMore` — **required.** Whether more pages exist. When `false`, `loadMore` never fires and the\n load-more skeleton is hidden. Required rather than defaulted so a missing binding cannot silently\n disable infinite loading.\n- `maxHeight` — bounds the internal scroll region (default `'70vh'`); the body shrinks to fit fewer\n rows. In `fillHeight` mode it acts as a cap instead.\n- `fillHeight` — stretch to fill the parent's height rather than sizing to the content. The body takes\n the space left after the header. Default `false`.\n- `loading` — first-load skeleton (full body). `loadingMore` — skeleton rows appended below.\n- `skeletonRowCount` — number of skeleton rows rendered for the first-load state (default `10`).\n `loadMoreSkeletonCount` — number of skeleton rows appended below the loaded rows while `loadingMore`\n is `true` (default `3`). Both clamp a zero or negative value up to `1`, so they cannot produce an\n empty or broken skeleton render.\n- `selectable` — opt-in row selection via a checkbox column (default `false`).\n- `rowsClickable` — make rows activatable (default `false`). Off, rows have no tab stop, no pointer\n cursor, no hover highlight, and `rowClick` never fires. On, a focused row also activates on Enter or\n Space, same as a click.\n- `trackBy` / `rowIdKey` — stable row identity. Strongly recommended: without either, identity falls\n back to the data index, which breaks selection after a reorder or prepend and disables the automatic\n scroll reset described below.\n\n## Column widths\n\n`styles.width` is the authoritative column width, applied as `flex-basis`, `min-width` **and**\n`max-width`. A column is exactly this wide in both the header and the body; it never grows or shrinks,\nand content that does not fit is clipped. That rigidity is what keeps the header and body scroll\ncontainers pixel-aligned. Default `160px`. Leftover row width is absorbed by a trailing spacer rather\nthan distributed across the columns.\n\n## Virtual scroll buffer\n\n`minBufferPx` and `maxBufferPx` control how far ahead of the visible viewport CDK keeps rows rendered.\nLeft unset, both derive from `rowHeight` — `minBufferPx` defaults to ~5 rows, `maxBufferPx` to ~10 —\nbecause a fixed pixel default means a different number of rows at every row height (CDK's own\ndefaults, 100px/200px, are ~1.8/~3.6 rows at a 56px row but under 1/2 rows at a 140px one).\n\nSymptom to watch for: fast vertical scrolling briefly shows a blank band before rows paint in. That\nmeans the rendered range is being outrun — raise `minBufferPx` and/or `maxBufferPx` to render further\nahead. Lower them to trade some of that smoothness for less off-screen DOM. If you set `minBufferPx`\nabove the derived `maxBufferPx` default, the component clamps `maxBufferPx` up to match — CDK requires\n`maxBufferPx >= minBufferPx` and throws in dev mode otherwise.\n\n## Theming the header background\n\n`--ui-ist-header-bg` is the supported way to recolor the header. Set it on the `ui-infinite-scroll-table`\nhost element itself, or on any ancestor — custom properties inherit, so a wrapper's `:host` works just\nas well. The component never redeclares a default value internally, so your value always wins. It\ncovers the header element, every header cell, and the header's trailing filler together, so you never\nneed to target internals or pierce encapsulation to get a consistent header color. Defaults to white\nwhen nothing sets it.\n\n## Lazily-loaded columns\n\n`columns` may change at runtime — pass a new array and the header and body re-render together, with the\nvirtualized viewport re-measured for you.\n\nWhile a column set is still arriving, set `loadingColumnCount` to the number of columns you are waiting\nfor. Each renders a skeleton in its header cell and in every body cell, since the column's identity is\nnot yet known. Set it back to `0` when the real columns land.\n\nPlaceholders are deliberately not part of `columns`, which means they cannot change which column is\nlast — so a `isSticky` column at the end keeps its right pin while they are on screen. Note the\ncorollary for your own column updates: because `isSticky` is positional, a pinned column must _stay_\nlast in `columns`, or it silently loses its pin (with a dev-mode warning).\n\n## Events\n\n`rowClick` emits `{ row, index }` and `selectionChange` emits `{ row, index, selected }`. `row` is your\nown object **by reference**, so class prototypes survive and a row's own `index` or `selected` field is\nnever shadowed.\n\n`rowClick` does not fire when the click or keypress originated inside an interactive element (`a`,\n`button`, `input`, `select`, `textarea`, `[role=\"button\"]`, `[tabindex]`), or while text is selected.\nAction buttons in cell templates therefore need no `stopPropagation()`.\n\n## Row selection\n\nSelection is opt-in and consumer-managed. Set `selectable` to prepend a checkbox column (pinned left).\nEach row's checkbox reflects the row's `selected` property; toggling it emits `selectionChange`. Update\nyour data in response — the component reflects state, it never mutates your rows. Since `data` must\nchange by reference, toggling one row means replacing that row (and the array) rather than mutating it.\n\n## Scrolling\n\n`scrollToIndex(index, behavior?)` and `scrollToTop(behavior?)` are public methods. They no-op only when no\nrows are rendered — the empty state, or an error with no rows; an error _with_ rows loaded keeps its\nviewport, so both still work there.\n\nThe table scrolls back to the top automatically on either of two signals:\n\n- **`loading` goes `true`.** The body becomes skeletons, so the scroll belongs at the top immediately.\n Works with no configuration — this is the path a filter or sort change normally takes.\n- **The first row's identity changes**, which catches a data set swapped in _without_ a `loading` pass.\n Only this second path needs `trackBy` or `rowIdKey`; without a stable identity it falls back to the row\n index, where the head is always `0` and a replacement is undetectable.\n\nA newest-first feed that _prepends_ rows also changes the head and will therefore reset; such consumers\nshould provide a stable identity and call `scrollToTop()` themselves when they want it.\n\n## `loadMore` semantics\n\n`loadMore` fires when the user scrolls within `loadMoreThreshold` **visible** rows of the end, **only**\nwhen `hasMore && !loading && !loadingMore && !error`. `loadMoreThreshold` counts down from the end of the\nvisible viewport, not the virtualized rendered range — the rendered range includes CDK's off-screen\nread-ahead buffer, so measuring it instead would conflate the user approaching the end with CDK merely\npre-rendering it ahead of time.\n\nIt emits **at most once per page that lands**. Any of three things re-arms it: the user scrolling back out\nof the trigger zone, a page arriving (`data` growing since the last emission), or the trigger zone being\nimpossible to leave at all — which is what fills an under-full viewport without user interaction. Because a\nlanded page re-arms on its own, page size does not have to be tuned against `loadMoreThreshold` or\n`loadMoreSkeletonCount`; a consumer parked at the bottom of a list with more pages keeps paging, which is\nwhat infinite scroll is expected to do.\n\n`loadMoreThreshold` is capped internally at one screenful of rows. A threshold wider than the viewport\ncannot mean \"approaching the end\", and uncapped it would put every scroll position inside the trigger zone.\nThe practical consequence is a bound on unattended paging: automatic filling stops once the loaded content\nreaches roughly twice the viewport height, after which paging follows the user's scrolling.\n\n## Empty and error states\n\nSupply `noDataTemplate` and `errorTemplate` to control these. Without them, both states render a\nbuilt-in fallback message, so a failed fetch never presents as an unexplained empty box. An error with\nrows already loaded renders below the rows; an error with no rows replaces the body.\n\n## End of the list\n\n`endOfListTemplate` is opt-in and, unlike the two states above, has **no built-in fallback** — supply\nnothing and nothing renders. It shows once `hasMore` goes `false` with rows already loaded (and the\ntable isn't mid-fetch or in an error state). The asymmetry with `noDataTemplate` / `errorTemplate` is\ndeliberate: a short list whose first page is the entire dataset would otherwise flash \"no more results\"\nthe instant it loads, which reads as noise rather than a useful signal — so this one stays silent unless\nyou explicitly ask for it.\n\n## Accessibility\n\n`role=\"table\"` on the root, `role=\"columnheader\"` on header cells, and `role=\"cell\"` on body cells —\nunconditionally, in every configuration. There is no `aria-selected` and no `aria-multiselectable`\nanywhere: selection is conveyed to assistive tech by the row's real checkbox (which carries its own\nchecked state and `aria-label`), not by a row-level ARIA attribute.\n\n`aria-rowcount` reports `-1` while more pages may load, `aria-busy` tracks the loading states, and\nloading is announced through a polite live region.\n\nRows are reached with Tab, not arrow keys — the component does not implement roving-tabindex grid\nnavigation. That is a deliberate non-goal, not an omission: `grid` carries an implicit contract of\narrow-key cell traversal this component does not fulfil, and `aria-multiselectable` is not valid on\n`role=\"table\"` in the first place. Because rows are virtualized, only the rendered window exists in the\nDOM; for rich keyboard traversal of very large datasets, prefer focusable controls inside cells.\n","whenToUse":[],"whenNotToUse":[],"template":"<!-- role=\"table\", not \"grid\": row access is Tab-only with no arrow-key cell traversal, so \"grid\" would\n promise 2D keyboard navigation this does not implement. Selection is conveyed by the row's checkbox,\n not `aria-selected` — which is valid only on grid/treegrid anyway. -->\n<div\n class=\"ui-ist\"\n role=\"table\"\n [class.ui-ist--condensed]=\"isCondensed()\"\n [class.ui-ist--scrolled-start]=\"!scrollStart()\"\n [class.ui-ist--scrolled-end]=\"horizontalScroll() && !scrollEnd()\"\n [class.ui-ist--fill-height]=\"fillHeight()\"\n [attr.aria-rowcount]=\"hasMore() ? -1 : data().length + 1\"\n [attr.aria-busy]=\"loading() || loadingMore()\"\n>\n <!-- Pinned header, outside the viewport so it never scrolls vertically. The outer element reserves the\n body's scrollbar width as padding (keeping the background and border across that strip); the inner\n one carries the horizontal scrollLeft synced from the body. -->\n <div class=\"ui-ist__header\" [style.padding-right.px]=\"scrollbarWidth()\" role=\"presentation\">\n <div #headerScroll class=\"ui-ist__header-scroll\" role=\"presentation\">\n <div class=\"ui-ist__row ui-ist__row--header\" role=\"row\" aria-rowindex=\"1\">\n @if (selectable()) {\n <div\n class=\"ui-ist__cell ui-ist__cell--header ui-ist__cell--checkbox ui-ist__cell--sticky-start\"\n [class.ui-ist__cell--sticky-start-edge]=\"!stickyStartKey()\"\n role=\"columnheader\"\n ></div>\n }\n @for (column of columns(); track column.key; let colIndex = $index) {\n <div\n class=\"ui-ist__cell ui-ist__cell--header\"\n role=\"columnheader\"\n [class.ui-ist__cell--sticky-start]=\"column.key === stickyStartKey()\"\n [class.ui-ist__cell--sticky-start-edge]=\"isStickyStartEdge(column)\"\n [class.ui-ist__cell--sticky-end]=\"column.key === stickyEndKey()\"\n [class.ui-ist__cell--right]=\"column.styles?.alignment === 'right'\"\n [class.ui-ist__cell--center]=\"column.styles?.alignment === 'center'\"\n [style.flex-basis]=\"columnWidth(column)\"\n [style.min-width]=\"columnWidth(column)\"\n [style.max-width]=\"columnWidth(column)\"\n [style.left]=\"stickyStartLeft(column)\"\n >\n @if (column.headerCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.headerCellTemplate; context: { $implicit: column.title, column, colIndex }\"\n ></ng-container>\n } @else {\n <span class=\"ui-ist__cell-text\">{{ column.title }}</span>\n }\n </div>\n }\n @for (loadingKey of loadingColumns(); track loadingKey) {\n <div\n class=\"ui-ist__cell ui-ist__cell--header ui-ist__cell--loading\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.flex-basis]=\"defaultColumnWidth\"\n [style.min-width]=\"defaultColumnWidth\"\n [style.max-width]=\"defaultColumnWidth\"\n >\n <ui-skeleton class=\"ui-ist__skeleton\" [count]=\"1\" [theme]=\"skeletonTheme\" appearance=\"line\"></ui-skeleton>\n </div>\n }\n <div class=\"ui-ist__cell ui-ist__cell--spacer\" role=\"presentation\" aria-hidden=\"true\"></div>\n </div>\n </div>\n </div>\n\n <!-- The CDK viewport owns BOTH vertical (virtualized) and horizontal scroll, so sticky columns pin\n against it and the horizontal scrollbar sits at the viewport bottom. -->\n @if (showViewport()) {\n <cdk-virtual-scroll-viewport\n class=\"ui-ist__body\"\n role=\"rowgroup\"\n [itemSize]=\"rowHeight()\"\n [minBufferPx]=\"effectiveMinBufferPx()\"\n [maxBufferPx]=\"effectiveMaxBufferPx()\"\n [style.height]=\"viewportHeight()\"\n [style.max-height]=\"fillHeight() ? maxHeight() : null\"\n >\n <div\n *cdkVirtualFor=\"let row of renderRows(); let rowIndex = index; trackBy: renderRowTrackBy\"\n class=\"ui-ist__row\"\n role=\"row\"\n [class.ui-ist__row--clickable]=\"rowsClickable() && row.kind === 'data'\"\n [class.ui-ist__row--selected]=\"isRowSelected(row)\"\n [attr.aria-rowindex]=\"row.kind === 'data' ? row.index + 2 : null\"\n [attr.aria-hidden]=\"row.kind === 'skeleton' ? true : null\"\n [attr.tabindex]=\"rowsClickable() && row.kind === 'data' ? 0 : null\"\n [style.height.px]=\"rowHeight()\"\n (click)=\"emitRowClick($event, row)\"\n (keydown.enter)=\"activateRow($event, row)\"\n (keydown.space)=\"activateRow($event, row)\"\n >\n @if (selectable()) {\n <!-- This `stopPropagation` is what actually stops a checkbox click activating the row: it runs\n before the click reaches the row's listener, so `isInteractiveTarget` is never consulted on\n this path. That guard is a real second line of defence (`ui-checkbox` renders a focusable\n wrapper and a native input, both matching INTERACTIVE_TARGET_SELECTOR), not a substitute. -->\n <div\n class=\"ui-ist__cell ui-ist__cell--checkbox ui-ist__cell--sticky-start\"\n [class.ui-ist__cell--sticky-start-edge]=\"!stickyStartKey()\"\n role=\"cell\"\n (click)=\"$event.stopPropagation()\"\n (keydown)=\"$event.stopPropagation()\"\n >\n @if (row.kind === 'data') {\n <ui-checkbox\n [checked]=\"isRowSelected(row)\"\n [ariaLabel]=\"'Select row ' + (row.index + 1)\"\n (changed)=\"emitSelectionChange(row, $event)\"\n ></ui-checkbox>\n }\n </div>\n }\n @for (column of columns(); track column.key) {\n <div\n class=\"ui-ist__cell\"\n role=\"cell\"\n [class.ui-ist__cell--sticky-start]=\"column.key === stickyStartKey()\"\n [class.ui-ist__cell--sticky-start-edge]=\"isStickyStartEdge(column)\"\n [class.ui-ist__cell--sticky-end]=\"column.key === stickyEndKey()\"\n [class.ui-ist__cell--right]=\"column.styles?.alignment === 'right'\"\n [class.ui-ist__cell--center]=\"column.styles?.alignment === 'center'\"\n [style.flex-basis]=\"columnWidth(column)\"\n [style.min-width]=\"columnWidth(column)\"\n [style.max-width]=\"columnWidth(column)\"\n [style.left]=\"stickyStartLeft(column)\"\n >\n @if (row.kind === 'skeleton') {\n <ui-skeleton class=\"ui-ist__skeleton\" [count]=\"1\" [theme]=\"skeletonTheme\" appearance=\"line\"></ui-skeleton>\n } @else if (column.rowCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.rowCellTemplate; context: { $implicit: row.item, rowIndex: row.index }\"\n ></ng-container>\n } @else {\n <span class=\"ui-ist__cell-text\">{{ getCellValue(row.item, column.key) }}</span>\n }\n </div>\n }\n @for (loadingKey of loadingColumns(); track loadingKey) {\n <div\n class=\"ui-ist__cell ui-ist__cell--loading\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.flex-basis]=\"defaultColumnWidth\"\n [style.min-width]=\"defaultColumnWidth\"\n [style.max-width]=\"defaultColumnWidth\"\n >\n <ui-skeleton class=\"ui-ist__skeleton\" [count]=\"1\" [theme]=\"skeletonTheme\" appearance=\"line\"></ui-skeleton>\n </div>\n }\n <div class=\"ui-ist__cell ui-ist__cell--spacer\" role=\"presentation\" aria-hidden=\"true\"></div>\n </div>\n </cdk-virtual-scroll-viewport>\n }\n\n <!-- `showErrorFooter()` and `showErrorState()` differ only on `hasData()`, so they are mutually\n exclusive and safe to merge here. Both fall back to a plain-language message, so a failed fetch is\n never a silent blank box. -->\n @if (showErrorFooter() || showErrorState()) {\n <div class=\"ui-ist__state\" role=\"alert\">\n @if (errorTemplate(); as errorTpl) {\n <ng-container *ngTemplateOutlet=\"errorTpl; context: errorContext\"></ng-container>\n } @else {\n <span>{{ translationContext + 'LOAD_ERROR' | uiTranslate | async }}</span>\n }\n </div>\n }\n\n @if (showEmptyState()) {\n <div class=\"ui-ist__state\">\n @if (noDataTemplate(); as emptyTpl) {\n <ng-container *ngTemplateOutlet=\"emptyTpl\"></ng-container>\n } @else {\n <span>{{ translationContext + 'NO_ROWS' | uiTranslate | async }}</span>\n }\n </div>\n }\n\n <!-- Opt-in, with no fallback (see `endOfListTemplate`). Not role=\"alert\": reaching the end is not an\n alert, and `aria-rowcount` already conveys completeness to assistive tech. -->\n @if (showEndOfList() && endOfListTemplate(); as endTpl) {\n <div class=\"ui-ist__state\">\n <ng-container *ngTemplateOutlet=\"endTpl\"></ng-container>\n </div>\n }\n\n <span class=\"ui-ist__sr-only\" role=\"status\" aria-live=\"polite\">\n @if (statusMessageKey(); as statusKey) {\n {{ statusKey | uiTranslate | async }}\n }\n </span>\n</div>\n","queryGuide":{"roles":["table","presentation","row","columnheader","rowgroup","cell","alert","status"],"ariaAttributes":["aria-rowindex","aria-hidden","aria-live","aria-rowcount","aria-busy"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-skeleton","ui-checkbox"],"suggestedQueries":["screen.getByRole('table')","screen.getByRole('presentation')","screen.getByRole('row')","screen.getByRole('columnheader')","screen.getByRole('rowgroup')","screen.getByRole('cell')","screen.getByRole('alert')","screen.getByRole('status')"]},"filePath":"components/infinite-scroll-table/infinite-scroll-table.component.ts","supportsTheme":false,"lastModified":1786626756402.4395},"inline-field":{"name":"InlineFieldComponent","selector":"ui-inline-field","category":"forms","description":"The Inline Field component enables users to make quick edits to text content with explicit save/cancel confirmation, without navigating to a separate form or modal.","importPath":"@testgorilla/tgo-ui/components/inline-field","moduleName":"InlineFieldComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"id","type":"unknown","defaultValue":"`inline-field-${inlineFieldID++}`","required":false,"alias":null,"transform":null,"description":"Component ID for accessibility","isSignal":true},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Field label","isSignal":true},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Field placeholder","isSignal":true},{"name":"value","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Current value","isSignal":true},{"name":"required","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the field is required","isSignal":true},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the field is disabled","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Application theme","isSignal":true},{"name":"fieldType","type":"FieldType","defaultValue":"'text'","required":false,"alias":null,"transform":null,"description":"Field type","isSignal":true},{"name":"maxRows","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Field type","isSignal":true},{"name":"errors","type":"string[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Error messages","isSignal":true},{"name":"showErrors","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Whether to show errors","isSignal":true},{"name":"buttonConfig","type":"InlineFieldButtonConfig","defaultValue":"{\n saveLabel: 'Save',\n cancelLabel: 'Cancel',\n saveIcon: 'Check-filled',\n cancelIcon: 'Close-filled',\n }","required":false,"alias":null,"transform":null,"description":"Button configuration","isSignal":true},{"name":"validation","type":"InlineFieldValidation","defaultValue":"{}","required":false,"alias":null,"transform":null,"description":"Validation configuration","isSignal":true},{"name":"ariaLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"ARIA label","isSignal":true},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Full width","isSignal":true}],"outputs":[{"name":"save","eventType":"InlineFieldChangeEvent","alias":null,"description":"Emitted when save button is clicked","isSignal":true},{"name":"cancel","eventType":"void","alias":null,"description":"Emitted when cancel button is clicked","isSignal":true},{"name":"valueChange","eventType":"string","alias":null,"description":"Emitted when value changes","isSignal":true},{"name":"editingModeChange","eventType":"boolean","alias":null,"description":"Emitted when editing mode changes","isSignal":true},{"name":"focusEvent","eventType":"void","alias":null,"description":"Emitted on focus","isSignal":true},{"name":"blurEvent","eventType":"void","alias":null,"description":"Emitted on blur","isSignal":true}],"module":{"name":"InlineFieldComponentModule","imports":["CommonModule","FormsModule","ReactiveFormsModule","MatInputModule","ButtonComponentModule","FieldComponentModule","IconComponentModule","InlineFieldComponent"],"exports":["InlineFieldComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"InlineFieldState","value":"'default' | 'focused' | 'editing' | 'error' | 'disabled' | 'loading'"}],"interfaces":[{"name":"InlineFieldEvents","properties":[{"name":"onSave","type":"EventEmitter<string>","optional":false},{"name":"onCancel","type":"EventEmitter<void>","optional":false},{"name":"onValueChange","type":"EventEmitter<string>","optional":false},{"name":"onEditingModeChange","type":"EventEmitter<boolean>","optional":false},{"name":"onFocus","type":"EventEmitter<void>","optional":false},{"name":"onBlur","type":"EventEmitter<void>","optional":false}]},{"name":"InlineFieldButtonConfig","properties":[{"name":"saveLabel","type":"string","optional":true},{"name":"cancelLabel","type":"string","optional":true},{"name":"saveIcon","type":"IconName","optional":true},{"name":"cancelIcon","type":"IconName","optional":true},{"name":"showOnHoverOnly","type":"boolean","optional":true}]},{"name":"InlineFieldValidation","properties":[{"name":"required","type":"boolean","optional":true},{"name":"minLength","type":"number","optional":true},{"name":"maxLength","type":"number","optional":true},{"name":"pattern","type":"RegExp","optional":true},{"name":"customValidator","type":"(value: string) => boolean","optional":true}]},{"name":"InlineFieldChangeEvent","properties":[{"name":"value","type":"string","optional":false},{"name":"saved","type":"boolean","optional":false},{"name":"previousValue","type":"string","optional":false}]}],"enums":[]},"examples":[],"documentation":"# Inline Field Component\n\nThe Inline Field component enables users to make quick edits to text content with explicit save/cancel confirmation, without navigating to a separate form or modal.\n\n## When to use\n\n- **Quick edits** to short text (titles, labels, single-line fields) inside pages, lists, tables, or cards\n- **Edits that can affect other data**, need validation, or could be destructive and require save/cancel confirmation\n- **Inside compound controls** (e.g., tags/suggestions box) where users switch between viewing and editing modes\n- **Renaming items** in lists, tables, or card layouts\n\n## When NOT to use\n\n- **Long, multi-step edits** → use a dedicated form or modal instead\n- **Trivial changes** that can auto-save safely → use standard input with autosave pattern\n- **Complex form fields** with multiple inputs → use full form components\n\n## Installation & Import\n\n```typescript\nimport { InlineFieldComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [InlineFieldComponentModule],\n})\nexport class YourModule {}\n```\n\n## Basic Usage\n\n### Simple Inline Field\n\n```html\n<ui-inline-field\n label=\"Title\"\n [(value)]=\"title\"\n placeholder=\"Enter title\"\n (save)=\"onSave($event)\"\n (cancel)=\"onCancel()\"\n>\n</ui-inline-field>\n```\n\n### Required Field with Validation\n\n```html\n<ui-inline-field\n label=\"Email Address\"\n [(value)]=\"email\"\n [required]=\"true\"\n [validation]=\"{\n required: true,\n pattern: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n }\"\n placeholder=\"Enter email address\"\n (save)=\"onEmailSave($event)\"\n>\n</ui-inline-field>\n```\n\n### Different Field Types\n\n```html\n<ui-inline-field\n label=\"Password\"\n [(value)]=\"password\"\n [fieldType]=\"'password'\"\n placeholder=\"Enter password\"\n (save)=\"onPasswordSave($event)\"\n>\n</ui-inline-field>\n```\n\n### Custom Button Labels\n\n```html\n<ui-inline-field\n label=\"Assessment Name\"\n [(value)]=\"assessmentName\"\n [buttonConfig]=\"{\n saveLabel: 'Apply',\n cancelLabel: 'Discard',\n saveIcon: 'Book-filled',\n cancelIcon: 'Upload-filled'\n }\"\n (save)=\"onAssessmentNameSave($event)\"\n>\n</ui-inline-field>\n```\n\n## API Reference\n\n### Inputs\n\n| Property | Type | Default | Description |\n| ------------------ | ------------------------- | -------------------------- | ---------------------------------------- |\n| `id` | `string` | `'inline-field-{counter}'` | Unique component ID for accessibility |\n| `label` | `string` | `''` | Field label displayed above the input |\n| `value` | `string` | `''` | Current field value |\n| `placeholder` | `string` | `''` | Placeholder text when field is empty |\n| `required` | `boolean` | `false` | Whether the field is required |\n| `disabled` | `boolean` | `false` | Whether the field is disabled |\n| `fieldType` | `FieldType` | `'text'` | Field type (text, password, email, etc.) |\n| `maxRows` | `number` | `0` | Maximum rows for textarea fields |\n| `applicationTheme` | `ApplicationTheme` | `'light'` | Application theme (light/dark/classic) |\n| `errors` | `string[]` | `[]` | Array of error messages to display |\n| `showErrors` | `boolean` | `true` | Whether to display error messages |\n| `fullWidth` | `boolean` | `false` | Whether field takes full container width |\n| `ariaLabel` | `string` | `''` | ARIA label for accessibility |\n| `buttonConfig` | `InlineFieldButtonConfig` | See below | Save/cancel button configuration |\n| `validation` | `InlineFieldValidation` | `{}` | Validation rules configuration |\n\n### Button Configuration (`InlineFieldButtonConfig`)\n\n| Property | Type | Default | Description |\n| ----------------- | ---------- | ---------------- | ------------------------------------------------- |\n| `saveLabel` | `string` | `'Save'` | Save button label |\n| `cancelLabel` | `string` | `'Cancel'` | Cancel button label |\n| `saveIcon` | `IconName` | `'Check-filled'` | Save button icon name |\n| `cancelIcon` | `IconName` | `'Close-filled'` | Cancel button icon name |\n| `showOnHoverOnly` | `boolean` | `false` | Show buttons only on hover (styling support only) |\n\n**Note:** Buttons are automatically styled with `secondary-inverted` variant and `small` size.\n\n### Validation Configuration (`InlineFieldValidation`)\n\n| Property | Type | Description |\n| ----------------- | ----------------------------------- | -------------------------- |\n| `required` | `boolean` | Field is required |\n| `minLength` | `number` | Minimum character length |\n| `maxLength` | `number` | Maximum character length |\n| `pattern` | `RegExp` | Validation pattern |\n| `customValidator` | `(value: string) => string \\| null` | Custom validation function |\n\n### Outputs\n\n| Event | Type | Description |\n| ------------------- | ------------------------ | ------------------------------------- |\n| `save` | `InlineFieldChangeEvent` | Emitted when save button is clicked |\n| `cancel` | `void` | Emitted when cancel button is clicked |\n| `valueChange` | `string` | Emitted when field value changes |\n| `editingModeChange` | `boolean` | Emitted when editing mode toggles |\n| `focusEvent` | `void` | Emitted when field receives focus |\n| `blurEvent` | `void` | Emitted when field loses focus |\n\n### Change Event (`InlineFieldChangeEvent`)\n\n```typescript\ninterface InlineFieldChangeEvent {\n value: string; // The new value\n saved: boolean; // Whether change was saved (true) or cancelled (false)\n previousValue: string; // Previous value before the change\n}\n```\n\n## Examples\n\n### Form Integration\n\n```typescript\n// Component\nexport class MyComponent {\n userForm = this.fb.group({\n firstName: ['', [Validators.required]],\n lastName: ['', [Validators.required, Validators.minLength(2)]],\n email: ['', [Validators.required, Validators.email]],\n });\n\n onFieldSave(field: string, event: InlineFieldChangeEvent) {\n this.userForm.patchValue({ [field]: event.value });\n // Perform API save\n this.saveUser(this.userForm.value);\n }\n\n getFieldErrors(fieldName: string): string[] {\n const control = this.userForm.get(fieldName);\n if (control?.errors && control?.touched) {\n const errors = [];\n if (control.errors['required']) errors.push('This field is required');\n if (control.errors['minlength']) errors.push('Minimum length not met');\n if (control.errors['email']) errors.push('Invalid email format');\n return errors;\n }\n return [];\n }\n}\n```\n\n```html\n<!-- Template -->\n<div class=\"user-profile\">\n <ui-inline-field\n label=\"First Name\"\n [value]=\"userForm.get('firstName')?.value\"\n [required]=\"true\"\n [validation]=\"{ required: true }\"\n [errors]=\"getFieldErrors('firstName')\"\n (save)=\"onFieldSave('firstName', $event)\"\n >\n </ui-inline-field>\n\n <ui-inline-field\n label=\"Last Name\"\n [value]=\"userForm.get('lastName')?.value\"\n [required]=\"true\"\n [validation]=\"{ required: true, minLength: 2 }\"\n [errors]=\"getFieldErrors('lastName')\"\n (save)=\"onFieldSave('lastName', $event)\"\n >\n </ui-inline-field>\n\n <ui-inline-field\n label=\"Email\"\n [value]=\"userForm.get('email')?.value\"\n [required]=\"true\"\n [validation]=\"{ \n required: true,\n pattern: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n }\"\n [errors]=\"getFieldErrors('email')\"\n (save)=\"onFieldSave('email', $event)\"\n >\n </ui-inline-field>\n</div>\n```\n\n### List Item Editing\n\n```html\n<div class=\"criteria-list\">\n @for (let criterion of criteria; trackBy: trackByCriterionId) {\n <div class=\"criterion-item\">\n <ui-checkbox [(ngModel)]=\"criterion.selected\"> </ui-checkbox>\n\n <ui-inline-field\n [value]=\"criterion.name\"\n placeholder=\"Enter criterion name\"\n [fullWidth]=\"true\"\n [buttonConfig]=\"{\n saveLabel: 'Save',\n cancelLabel: 'Cancel'\n }\"\n (save)=\"updateCriterion(criterion.id, $event.value)\"\n (cancel)=\"onCancelEdit()\"\n >\n </ui-inline-field>\n </div>\n }\n</div>\n```\n\n### Custom Validation\n\n```typescript\nexport class ValidationExample {\n // Custom validation function\n validateSkillName = (value: string): string | null => {\n if (!value) return 'Skill name is required';\n if (value.length < 3) return 'Skill name must be at least 3 characters';\n if (this.existingSkills.includes(value.toLowerCase())) {\n return 'This skill already exists';\n }\n return null;\n };\n\n existingSkills = ['javascript', 'typescript', 'angular'];\n}\n```\n\n```html\n<ui-inline-field\n label=\"Skill Name\"\n [value]=\"newSkillName\"\n [validation]=\"{\n customValidator: validateSkillName\n }\"\n placeholder=\"Enter skill name\"\n (save)=\"addSkill($event.value)\"\n>\n</ui-inline-field>\n```\n\n## Keyboard Navigation\n\nThe Inline Field component supports comprehensive keyboard navigation:\n\n- **Click** or **Focus** on the field → Enter edit mode\n- **Escape** in edit mode → Cancel changes and exit edit mode\n- **Enter** in edit mode → Save changes and exit edit mode (except with Shift+Enter)\n- **Tab** in edit mode → Navigate to Save button → Cancel button → next focusable element\n- **Focus out** of component → Automatically cancel changes and exit edit mode\n\n## Accessibility\n\n### ARIA Support\n\n- `role=\"button\"` on display mode for screen readers\n- `aria-label` for describing the field purpose\n- `aria-describedby` linking to error messages\n- `aria-live=\"polite\"` for error announcements\n\n### Screen Reader Instructions\n\n- Clear instructions for keyboard navigation\n- Proper labeling of edit states\n- Error message announcements\n\n### High Contrast & Reduced Motion\n\n- High contrast mode support with enhanced borders\n- Respects `prefers-reduced-motion` settings\n- Keyboard focus indicators\n\n## Theming\n\n### Light Theme (Default)\n\n- White background with dark borders\n- Black text and focus indicators\n- Standard button styling\n\n### Dark Theme\n\n```html\n<ui-inline-field applicationTheme=\"dark\" label=\"Dark Theme Field\" [(value)]=\"value\"> </ui-inline-field>\n```\n\n### Classic Theme\n\n```html\n<ui-inline-field applicationTheme=\"classic\" label=\"Classic Theme Field\" [(value)]=\"value\"> </ui-inline-field>\n```\n\n## Best Practices\n\n### ✅ Do\n\n- Use for short, single-line text edits\n- Provide clear validation feedback\n- Use appropriate button labels for context\n- Implement proper error handling\n- Test keyboard navigation thoroughly\n- Use `fullWidth` for fields in constrained layouts\n- Provide meaningful ARIA labels for accessibility\n- Handle focus-out events appropriately\n\n### ❌ Don't\n\n- Use for complex, multi-field forms\n- Auto-save without explicit user confirmation\n- Use for very long text content\n- Skip validation for important fields\n- Ignore accessibility requirements\n- Use without proper error handling\n- Forget to handle the automatic cancel on focus-out\n\n## Migration from Standard Field\n\nIf migrating from a standard field to inline field:\n\n```typescript\n// Before: Standard field\n<ui-field\n label=\"Title\"\n [(ngModel)]=\"title\"\n (blur)=\"saveTitle()\">\n</ui-field>\n\n// After: Inline field\n<ui-inline-field\n label=\"Title\"\n [(value)]=\"title\"\n (save)=\"saveTitle($event.value)\">\n</ui-inline-field>\n```\n\n## Troubleshooting\n\n### Common Issues\n\n**Q: Buttons don't appear when editing**\nA: Ensure the component has enough space for button positioning. Check CSS overflow properties. Buttons are positioned absolutely below the field.\n\n**Q: Validation doesn't work**\nA: Verify validation configuration is properly set and error handling is implemented. Make sure to handle validation errors in the `save` event.\n\n**Q: Field exits edit mode unexpectedly**\nA: This is intended behavior when focus leaves the component. Use proper save handling to prevent data loss.\n\n**Q: Keyboard navigation issues**\nA: Ensure proper tab order and no conflicting keyboard event handlers. The component handles Enter, Escape, and Tab keys.\n\n**Q: Custom icons don't show**\nA: Ensure you're using valid `IconName` values from the icon library. Check that the icon names exist in your icon set.\n\nFor more help, check the Storybook examples or contact the design system team.\n","whenToUse":["**Quick edits** to short text (titles, labels, single-line fields) inside pages, lists, tables, or cards","**Edits that can affect other data**, need validation, or could be destructive and require save/cancel confirmation","**Inside compound controls** (e.g., tags/suggestions box) where users switch between viewing and editing modes","**Renaming items** in lists, tables, or card layouts"],"whenNotToUse":["**Long, multi-step edits** → use a dedicated form or modal instead","**Trivial changes** that can auto-save safely → use standard input with autosave pattern","**Complex form fields** with multiple inputs → use full form components"],"template":"<div class=\"inline-field-container\">\n <div class=\"field-wrapper\" [ngClass]=\"{ 'is-editing': isEditing() }\">\n <ui-field\n [id]=\"id() + '-input'\"\n [label]=\"label()\"\n [placeholder]=\"placeholder()\"\n [value]=\"currentValue()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [errors]=\"errors()\"\n [showBottomContent]=\"showErrors()\"\n [ariaLabel]=\"ariaLabel()\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [isAutocompleteOff]=\"true\"\n [type]=\"fieldType()\"\n [maxRows]=\"maxRows()\"\n (input)=\"onInputChange($event)\"\n (fieldBlur)=\"onInputBlur()\"\n (focusin)=\"onInputFocus()\"\n #fieldElement\n data-testid=\"inline-field\"\n ></ui-field>\n\n @if (isEditing()) {\n <div class=\"action-buttons\">\n <ui-button\n [label]=\"saveButtonConfig().label\"\n [iconName]=\"saveButtonConfig().iconName\"\n [variant]=\"saveButtonConfig().variant\"\n [size]=\"saveButtonConfig().size\"\n [disabled]=\"saveButtonConfig().disabled\"\n [applicationTheme]=\"applicationTheme()\"\n [iconPosition]=\"'left'\"\n (buttonClickEvent)=\"saveEdit()\"\n [attr.aria-label]=\"'Save ' + (label() || 'field')\"\n class=\"save-button\"\n ></ui-button>\n <ui-button\n [label]=\"cancelButtonConfig().label\"\n [iconName]=\"cancelButtonConfig().iconName\"\n [variant]=\"cancelButtonConfig().variant\"\n [size]=\"cancelButtonConfig().size\"\n [disabled]=\"cancelButtonConfig().disabled\"\n [applicationTheme]=\"applicationTheme()\"\n [iconPosition]=\"'left'\"\n (buttonClickEvent)=\"cancelEdit()\"\n [attr.aria-label]=\"'Cancel editing ' + (label() || 'field')\"\n class=\"cancel-button\"\n ></ui-button>\n </div>\n }\n </div>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label"],"dataTestIds":["inline-field"],"materialComponents":[],"childUIComponents":["ui-field","ui-button"],"suggestedQueries":["screen.getByLabelText('label text')","screen.getByTestId('inline-field')"]},"filePath":"components/inline-field/inline-field.component.ts","supportsTheme":true,"lastModified":1786626756402.9106},"logo":{"name":"LogoComponent","selector":"ui-logo","category":"logo","description":"Renders the TestGorilla logo mark and wordmark in approved configurations. Used in the Navbar, Side Navigation, and marketing-facing surfaces within the product. Ensures the logo is always displayed at the correct proportions and in an approved colour variant.","importPath":"@testgorilla/tgo-ui/components/logo","moduleName":"LogoComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"type","type":"LogoTypeEnum","defaultValue":"LogoTypeEnum.DEFAULT","required":false,"alias":null,"transform":null,"description":"Logo type","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"label","type":"unknown","defaultValue":"'POWERED BY'","required":false,"alias":null,"transform":null,"description":"Label. Only for ppoweredby type","isSignal":false},{"name":"width","type":"number","defaultValue":"200","required":false,"alias":null,"transform":null,"description":"Set logo width","isSignal":false},{"name":"allowResizing","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Allow change logo width","isSignal":false}],"outputs":[],"module":{"name":"LogoComponentModule","imports":["LogoComponent"],"exports":["LogoComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[{"name":"LogoTypeEnum","members":[{"name":"DEFAULT","value":"'default'"},{"name":"POWEREDBY","value":"'poweredby'"},{"name":"REVERSE","value":"'reverse'"},{"name":"PRIMARY_BRANDMARK_PINK","value":"'primary-brandmark-pink'"},{"name":"PRIMARY_LOCKUP_PINK","value":"'primary-lockup-pink'"},{"name":"PRIMARY_WORDMARK_PINK","value":"'primary-wordmark-pink'"},{"name":"SECONDARY_BRANDMARK_BLACK","value":"'secondary-brandmark-black'"},{"name":"SECONDARY_BRANDMARK_CREAM","value":"'secondary-brandmark-cream'"},{"name":"SECONDARY_LOCKUP_BLACK","value":"'secondary-lockup-black'"},{"name":"SECONDARY_LOCKUP_CREAM","value":"'secondary-lockup-cream'"},{"name":"SECONDARY_WORDMARK_BLACK","value":"'secondary-wordmark-black'"},{"name":"SECONDARY_WORDMARK_CREAM","value":"'secondary-wordmark-cream'"}]},{"name":"LogoPathEnum","members":[{"name":"DEFAULT","value":"'/images/testgorilla.svg'"},{"name":"POWEREDBY","value":"'/images/poweredbyTG.svg'"},{"name":"REVERSE","value":"'/images/reverseTG.svg'"},{"name":"PRIMARY_BRANDMARK_PINK","value":"'/images/rebrand-logo/primary_brandmark_pink.svg'"},{"name":"PRIMARY_LOCKUP_PINK","value":"'/images/rebrand-logo/primary_lockup_pink.svg'"},{"name":"PRIMARY_WORDMARK_PINK","value":"'/images/rebrand-logo/primary_wordmark_pink.svg'"},{"name":"SECONDARY_BRANDMARK_BLACK","value":"'/images/rebrand-logo/secondary_brandmark_black.svg'"},{"name":"SECONDARY_BRANDMARK_CREAM","value":"'/images/rebrand-logo/secondary_brandmark_cream.svg'"},{"name":"SECONDARY_LOCKUP_BLACK","value":"'/images/rebrand-logo/secondary_lockup_black.svg'"},{"name":"SECONDARY_LOCKUP_CREAM","value":"'/images/rebrand-logo/secondary_lockup_cream.svg'"},{"name":"SECONDARY_WORDMARK_BLACK","value":"'/images/rebrand-logo/secondary_wordmark_black.svg'"},{"name":"SECONDARY_WORDMARK_CREAM","value":"'/images/rebrand-logo/secondary_wordmark_cream.svg'"},{"name":"REBRAND_POWEREDBY","value":"'/images/rebrand-logo/powered-by.svg'"}]}]},"examples":[],"documentation":"Renders the TestGorilla logo mark and wordmark in approved configurations. Used in the Navbar, Side Navigation, and marketing-facing surfaces within the product. Ensures the logo is always displayed at the correct proportions and in an approved colour variant.\n\n## When to use\n\n- **Correct sizing and colour** — use the component rather than placing logo images manually, so proportions and variants are handled automatically\n- **Navigation and brand-identity surfaces** — use in the main navigation header and any surface that needs brand identity, such as onboarding screens and email preview headers\n- **Mark only vs. full wordmark** — choose the variant based on available space; the mark alone works in compact navbars\n\n## When NOT to use\n\n- **Modified proportions, colours, or whitespace** → don't alter them, the component enforces these constraints\n- **Body content or cards** → don't place the logo there; it belongs in global navigation and brand-identity surfaces only\n- **Home button without an anchor** → don't use the logo as a home button without wrapping it in an anchor element with proper accessibility\n\n## How to use\n\nTo use the Logo component, you need to import the `LogoComponentModule` in your module:\n\n```typescript\nimport { LogoComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n LogoComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-logo [type]=\"LogoTypeEnum.DEFAULT\" [applicationTheme]=\"theme\" [width]=\"200\"> </ui-logo>\n```\n","whenToUse":["**Correct sizing and colour** — use the component rather than placing logo images manually, so proportions and variants are handled automatically","**Navigation and brand-identity surfaces** — use in the main navigation header and any surface that needs brand identity, such as onboarding screens and email preview headers","**Mark only vs. full wordmark** — choose the variant based on available space; the mark alone works in compact navbars"],"whenNotToUse":["**Modified proportions, colours, or whitespace** → don't alter them, the component enforces these constraints","**Body content or cards** → don't place the logo there; it belongs in global navigation and brand-identity surfaces only","**Home button without an anchor** → don't use the logo as a home button without wrapping it in an anchor element with proper accessibility"],"template":"@if (applicationTheme === 'classic') {\n <img [attr.src]=\"logoURL\" [alt]=\"type\" />\n} @else {\n @if (label && type === LogoTypeEnum.POWEREDBY) {\n <div class=\"logo-label\" [style.width.px]=\"width\">\n <h3 class=\"bold\" [style.margin-left.px]=\"width * 0.175\" [style.font-size.px]=\"width * 0.1\">\n {{ label }}\n </h3>\n <img [attr.src]=\"logoURL\" [alt]=\"type\" [width]=\"width\" />\n </div>\n } @else {\n <img [attr.src]=\"logoURL\" [alt]=\"type\" [width]=\"width\" />\n }\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/logo/logo.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=192-2407","lastModified":1786626756403.369},"media-card":{"name":"MediaCardComponent","selector":"ui-media-card","category":"media-card","description":"A card variant designed to present media-rich content — typically video or image assets paired with metadata. Used in the test library and content browsing surfaces where candidates or hiring managers preview test formats, video questions, or skill demonstrations before making a selection.","importPath":"@testgorilla/tgo-ui/components/media-card","moduleName":"MediaCardComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"title","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"The title of the card","isSignal":false},{"name":"description","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"The description of the card","isSignal":false},{"name":"image","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"The image of the card","isSignal":false},{"name":"buttonLabel","type":"string","defaultValue":"''","required":true,"alias":null,"transform":null,"description":"The label of the button","isSignal":false}],"outputs":[{"name":"buttonClick","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A card variant designed to present media-rich content — typically video or image assets paired with metadata. Used in the test library and content browsing surfaces where candidates or hiring managers preview test formats, video questions, or skill demonstrations before making a selection.\n\n## When to use\n\n- **Media-led content** — use when the primary content of the card is a media asset, not text; the media leads, metadata follows\n- **Short titles and descriptions** — keep them brief, the media carries the context\n- **Grid layouts** — use in 2–3 columns on desktop so multiple options can be compared at a glance\n\n## When NOT to use\n\n- **Purely text-based content** → use the standard Card component instead\n- **Missing fallback state** → don't omit a placeholder for when media fails to load; show one rather than a broken layout\n- **Dense list views** → don't use Media Card where media thumbnails add more noise than value\n\n## How to use\n\nTo use the Media Card component, you need to import it in your component:\n\n```typescript\nimport { MediaCardComponent } from '@testgorilla/tgo-ui';\n\n@Component({\n // ... other component properties\n imports: [\n // ... other imports\n MediaCardComponent,\n ],\n // ...\n})\nexport class YourComponent {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-media-card\n title=\"Card Title\"\n description=\"Card description text goes here\"\n image=\"/path/to/image.svg\"\n buttonLabel=\"Click me\"\n (buttonClick)=\"onButtonClick()\"\n>\n</ui-media-card>\n```\n","whenToUse":["**Media-led content** — use when the primary content of the card is a media asset, not text; the media leads, metadata follows","**Short titles and descriptions** — keep them brief, the media carries the context","**Grid layouts** — use in 2–3 columns on desktop so multiple options can be compared at a glance"],"whenNotToUse":["**Purely text-based content** → use the standard Card component instead","**Missing fallback state** → don't omit a placeholder for when media fails to load; show one rather than a broken layout","**Dense list views** → don't use Media Card where media thumbnails add more noise than value"],"template":"<img [attr.src]=\"image\" alt=\"card image\" />\n\n<div class=\"media-card-content\">\n <div\n class=\"media-card-title\"\n [isMultiline]=\"true\"\n [matTooltip]=\"showTitleTooltip ? title : ''\"\n uiEllipseText\n (onChangeTextState)=\"showTitleTooltip = $event\"\n >\n {{ title }}\n </div>\n\n <div class=\"media-card-description\">\n {{ description }}\n </div>\n</div>\n\n<ui-divider [size]=\"'small'\"></ui-divider>\n\n<div class=\"media-card-action\">\n <ui-button [variant]=\"'ghost'\" [label]=\"buttonLabel\" (buttonClickEvent)=\"buttonClick.emit()\"></ui-button>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-divider","ui-button"],"suggestedQueries":[]},"filePath":"components/media-card/media-card.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1762-77327","lastModified":1786626756403.7495},"media-dialog":{"name":"MediaDialogComponent","selector":"ui-media-dialog","category":"dialog","description":"A dialog variant optimised for displaying rich media — video playback, image preview, or document display — in a focused overlay. Used in assessment setup to preview video questions, in the test library to play example test content, and in candidate results to replay interview recordings.","importPath":"@testgorilla/tgo-ui/components/media-dialog","moduleName":"MediaDialogComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"MediaDialogStep","properties":[{"name":"subheader","type":"string","optional":true},{"name":"title","type":"string","optional":false},{"name":"body","type":"string","optional":false},{"name":"mediaContainer","type":"TemplateRef<unknown>","optional":false},{"name":"primaryButtonLabel","type":"string","optional":false},{"name":"secondaryButtonLabel","type":"string","optional":true},{"name":"backButtonLabel","type":"string","optional":true},{"name":"primaryButtonDataTestId","type":"string","optional":true},{"name":"secondaryButtonDataTestId","type":"string","optional":true},{"name":"backButtonDataTestId","type":"string","optional":true}]},{"name":"MediaDialogData","properties":[{"name":"steps","type":"MediaDialogStep[]","optional":true}]}],"enums":[]},"examples":[],"documentation":"A dialog variant optimised for displaying rich media — video playback, image preview, or document display — in a focused overlay. Used in assessment setup to preview video questions, in the test library to play example test content, and in candidate results to replay interview recordings.\n\n## When to use\n\n- **Media assets only** — use Media Dialog exclusively when the content is a media asset, such as video, audio, image, or document preview\n- **Playback and close controls** — always include playback controls and a clearly visible close button\n- **Natural aspect ratio** — size the dialog to the natural aspect ratio of the media, not a fixed frame that clips or stretches it\n\n## When NOT to use\n\n- **Text-only or form content** → use Custom Dialog or Confirm Dialog instead\n- **Audio on autoplay** → always start muted or paused and let the user initiate playback\n- **Referencing the underlying page** → use a Side Sheet when the user needs to see the page while viewing media\n\n## How to use\n\nTo use the Media Dialog component, you need to import the `MediaDialogComponent` in your module:\n\n```typescript\nimport { MediaDialogComponent } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n MediaDialogComponent,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component with the DialogService:\n\n```typescript\nimport { DialogService } from '@testgorilla/tgo-ui';\n\n@Component({\n // ...\n})\nexport class YourComponent {\n constructor(private dialogService: DialogService) {}\n\n openMediaDialog(): void {\n const dialogConfig = {\n size: 'small',\n extraData: {\n steps: [\n ...\n ],\n }\n };\n\n this.dialogService\n .open(MediaDialogComponent, dialogConfig)\n .afterClosed()\n .subscribe(result => {\n // Handle dialog close\n });\n }\n}\n```\n","whenToUse":["**Media assets only** — use Media Dialog exclusively when the content is a media asset, such as video, audio, image, or document preview","**Playback and close controls** — always include playback controls and a clearly visible close button","**Natural aspect ratio** — size the dialog to the natural aspect ratio of the media, not a fixed frame that clips or stretches it"],"whenNotToUse":["**Text-only or form content** → use Custom Dialog or Confirm Dialog instead","**Audio on autoplay** → always start muted or paused and let the user initiate playback","**Referencing the underlying page** → use a Side Sheet when the user needs to see the page while viewing media"],"template":"<ng-container>\n @if (steps && steps.length > 1) {\n <ui-progress-bar [progress]=\"(currentStepIndex + 1) * (100 / steps.length)\"></ui-progress-bar>\n }\n\n <ui-dialog [title]=\"title\" (closeEvent)=\"dialogRef.close(null)\" [showCloseButton]=\"true\">\n @if (mediaContainer) {\n <div class=\"media-container\">\n <ng-container [ngTemplateOutlet]=\"mediaContainer\"></ng-container>\n </div>\n }\n\n @if (subheader) {\n <h4 class=\"subheader\">{{ subheader }}</h4>\n }\n\n @if (body) {\n <div [innerHTML]=\"body\"></div>\n }\n\n <div mat-dialog-actions class=\"footer\">\n <div>\n @if (backButtonLabel && currentStepIndex > 0) {\n <ui-button\n variant=\"text-inline\"\n size=\"medium\"\n [label]=\"backButtonLabel\"\n [attr.data-testid]=\"backButtonDataTestId\"\n (click)=\"onBackButtonClick()\"\n ></ui-button>\n }\n </div>\n\n <div class=\"action-buttons\">\n @if (secondaryButtonLabel) {\n <ui-button\n variant=\"ghost\"\n [label]=\"secondaryButtonLabel\"\n [attr.data-testid]=\"secondaryButtonDataTestId\"\n (click)=\"onSecondaryButtonClick()\"\n ></ui-button>\n }\n @if (primaryButtonLabel) {\n <ui-button\n variant=\"primary\"\n [label]=\"primaryButtonLabel\"\n [attr.data-testid]=\"primaryButtonDataTestId\"\n (click)=\"onPrimaryButtonClick()\"\n ></ui-button>\n }\n </div>\n </div>\n </ui-dialog>\n</ng-container>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-progress-bar","ui-dialog","ui-button"],"suggestedQueries":[]},"filePath":"components/media-dialog/media-dialog.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1762-77327","lastModified":1786626756404.0125},"multi-input":{"name":"MultiInputComponent","selector":"ui-multi-input","category":"multi-input","description":"A form field that accepts multiple discrete text values as chips in a single input. Used for adding multiple email addresses (candidate invitations), job titles, skills, or tags in a single entry field. Values are entered one at a time and displayed as removable chips.","importPath":"@testgorilla/tgo-ui/components/multi-input","moduleName":"MultiInputComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"itemsList","type":"Record<string, string | null | number>[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"itemTextField","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"itemValueField","type":"string","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"value","type":"MultiInputValue","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"required","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"allowDecimal","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"allowNegative","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"errors","type":"string[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxCharacters","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"validators","type":"ValidatorFn[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"hasCustomErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"valueChange","eventType":"MultiInputValue","alias":null,"description":"","isSignal":false}],"module":{"name":"MultiInputComponentModule","imports":["CommonModule","MatInputModule","MatSelectModule","DividerComponentModule","IconComponentModule","DropdownComponentModule","ReactiveFormsModule","DigitsOnlyDirective","HasValidationErrorPipe","ValidationErrorModule","FieldComponentModule","MultiInputComponent"],"exports":["MultiInputComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"MultiInputValue","value":"{\n value: T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- part of the same backward-compatible consumer contract as the generic default above\n [key: string]: any;\n}"}],"interfaces":[],"enums":[]},"examples":[{"name":"WithCustomValidationErrors","template":"<form [formGroup]=\"form\">\n <ui-multi-input\n formControlName=\"currency\"\n label=\"Currency\"\n itemTextField=\"label\"\n itemValueField=\"code\"\n [itemsList]=\"itemsList\"\n [required]=\"true\"\n [hasCustomErrors]=\"true\"\n applicationTheme=\"${appTheme}\"\n >\n <div custom-errors>\n <ui-validation-error\n [ngControl]=\"form.controls.currency\"\n [touchedOn]=\"form.controls.currency.touched\"\n label=\"Currency\"\n applicationTheme=\"${appTheme}\"\n ></ui-validation-error>\n </div>\n </ui-multi-input>\n </form>","props":{}}],"documentation":"A form field that accepts multiple discrete text values as chips in a single input. Used for adding multiple email addresses (candidate invitations), job titles, skills, or tags in a single entry field. Values are entered one at a time and displayed as removable chips.\n\n## When to use\n\n- **Open-ended sets of the same type** — when users need to enter values like emails, tags, or keywords with no fixed count\n- **A clear 'press Enter to add' affordance** — always show it so users understand the input model\n- **Per-chip validation** — validate each value as it's added and show inline errors per chip (e.g. invalid email format) rather than blocking submission\n\n## When NOT to use\n\n- **A defined option list** → use Autocomplete instead, giving users options to select rather than a blank field\n- **Unlimited chip growth** → don't allow it without truncation or a visible cap, as uncontrolled growth breaks layouts\n- **Single-value inputs** → use a standard Field instead\n\n## How to use\n\nTo use the Multi Input component, you need to import the `MultiInputComponentModule` in your module:\n\n```typescript\nimport { MultiInputComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n MultiInputComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-multi-input\n label=\"Currency\"\n itemTextField=\"label\"\n itemValueField=\"code\"\n [itemsList]=\"[\n { code: 'usd', label: 'USD ($)' },\n { code: 'eur', label: 'EUR (€)' }\n ]\"\n [value]=\"{ value: 100, code: 'usd' }\"\n applicationTheme=\"light\"\n>\n</ui-multi-input>\n```\n","whenToUse":["**Open-ended sets of the same type** — when users need to enter values like emails, tags, or keywords with no fixed count","**A clear 'press Enter to add' affordance** — always show it so users understand the input model","**Per-chip validation** — validate each value as it's added and show inline errors per chip (e.g. invalid email format) rather than blocking submission"],"whenNotToUse":["**A defined option list** → use Autocomplete instead, giving users options to select rather than a blank field","**Unlimited chip growth** → don't allow it without truncation or a visible cap, as uncontrolled growth breaks layouts","**Single-value inputs** → use a standard Field instead"],"template":"<div [formGroup]=\"multiInputForm\">\n <div class=\"controls-wrapper\">\n <ui-dropdown\n [hasError]=\"controlHasError\"\n [formControlName]=\"itemValueField()\"\n [valueList]=\"itemsList()\"\n [textField]=\"itemTextField()\"\n [valueField]=\"itemValueField()\"\n [showBottomContent]=\"false\"\n [allowClear]=\"false\"\n (closed)=\"markAsTouched()\"\n ></ui-dropdown>\n\n <ui-field\n [hasError]=\"controlHasError\"\n formControlName=\"value\"\n [label]=\"label()\"\n [placeholder]=\"placeholder()\"\n [required]=\"required()\"\n [hideBuiltInErrors]=\"true\"\n [showBottomContent]=\"false\"\n [allowOnlyDigits]=\"true\"\n [fullWidth]=\"fullWidth\"\n [allowNegative]=\"allowNegative()\"\n (focusout)=\"markAsTouched()\"\n ></ui-field>\n </div>\n\n @if (!hasCustomErrors()) {\n @if (!controlHasError && maxCharacters() && !errors()?.length) {\n <mat-hint class=\"info\">{{ getValueLength() }} / {{ maxCharacters() }}</mat-hint>\n }\n\n @if (errors()?.length || (ngControl?.errors | hasValidationError)) {\n <mat-hint class=\"error\">\n @for (error of errors(); track error) {\n <ui-icon name=\"Error-in-line\"></ui-icon>{{ error }}\n }\n @if ((ngControl?.errors | hasValidationError) && !hideBuiltInErrors()) {\n <ui-validation-error\n [touchedOn]=\"!controlHasError\"\n [ngControl]=\"ngControl\"\n [label]=\"hideLabelInErrors() ? null : label()\"\n ></ui-validation-error>\n }\n </mat-hint>\n }\n }\n <ng-content select=\"[custom-errors]\"></ng-content>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-hint"],"childUIComponents":["ui-dropdown","ui-field","ui-icon","ui-validation-error"],"suggestedQueries":[]},"filePath":"components/multi-input/multi-input.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=11557-22708","lastModified":1786626756404.5642},"navbar":{"name":"NavbarComponent","selector":"ui-navbar","category":"navbar","description":"The top-level horizontal navigation bar for the TestGorilla product. Provides primary navigation links, the logo, user account menu, and contextual actions (notifications, credits). Adapts responsively across desktop and mobile breakpoints. Used as the persistent chrome on all authenticated product pages.","importPath":"@testgorilla/tgo-ui/components/navbar","moduleName":"NavbarComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"routes","type":"IRoute[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Routes object","isSignal":true},{"name":"activedRoute","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Actived route id","isSignal":true},{"name":"userName","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"User first name and last name","isSignal":true},{"name":"email","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"User email","isSignal":true},{"name":"menuItems","type":"MenuItem[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Menu items to show in menu above the Log out button","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"this.defaultAppTheme ?? 'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":true},{"name":"contentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"It's ng-content for the collapsed (drawer) version","isSignal":true},{"name":"userInfoExtraTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Additional info template reference for the extra user info in the dropdown/sidesheet","isSignal":true},{"name":"menuContentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Custom content template reference for the menu section between user info and menu items.\nUse this to add custom components like credits display.","isSignal":true},{"name":"mobileBreakpoint","type":"unknown","defaultValue":"ScreenBreakpoints.MOBILE","required":false,"alias":null,"transform":null,"description":"Width at or below which the navbar renders its compact (brandmark) logo","isSignal":true},{"name":"collapseBreakpoint","type":"unknown","defaultValue":"ScreenBreakpoints.LARGE_TABLET","required":false,"alias":null,"transform":null,"description":"Width at or below which the horizontal navigation and the profile menu collapse\ninto the hamburger-triggered drawer","isSignal":true},{"name":"isAvatar","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show avatar with user initials","isSignal":true},{"name":"menuClass","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Sets class for the menu","isSignal":true}],"outputs":[{"name":"navigateEvent","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"menuItemClicked","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"logoutEvent","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"menuState","eventType":"MenuState","alias":null,"description":"","isSignal":true}],"module":{"name":"NavbarComponentModule","imports":["CommonModule","MatToolbarModule","MatIconModule","MatButtonModule","MatRippleModule","MatMenuModule","LogoComponentModule","IconComponentModule","UiTranslatePipe","AvatarComponentModule","SideSheetComponentModule","ButtonComponentModule","MobileNavbarSideSheetComponent","BadgeComponentModule","NavbarComponent"],"exports":["NavbarComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"ScreenSize","value":"'mobile' | 'tablet' | 'desktop'"}],"interfaces":[{"name":"IRoute","properties":[{"name":"id","type":"string","optional":false},{"name":"title","type":"string","optional":false},{"name":"badgeOptions","type":"RouteBadge","optional":true}]},{"name":"RouteBadge","properties":[{"name":"color","type":"RebrandBadgeColor","optional":false},{"name":"text","type":"string","optional":true},{"name":"variant","type":"BadgeVariant","optional":true},{"name":"notificationsAmount","type":"number","optional":true}]},{"name":"MenuItem","properties":[{"name":"id","type":"string","optional":false},{"name":"icon","type":"IconName","optional":false},{"name":"text","type":"string","optional":false}]},{"name":"MenuState","properties":[{"name":"screenSize","type":"ScreenSize","optional":false},{"name":"isOpened","type":"boolean","optional":false}]}],"enums":[]},"examples":[{"name":"WithContent","template":"<ng-template #temp>\n <ui-button applicationTheme=\"${appTheme}\" label=\"Press\"></ui-button>\n </ng-template>\n <ui-navbar applicationTheme=\"${appTheme}\"\n [contentTemplateRef]=\"temp\"\n [userName]=\"'Markus Letterman'\"\n [routes]=\"[\n {\n id: 'assessments',\n title: 'Assessments',\n },\n {\n id: 'results',\n title: 'Results',\n },\n ]\"\n [menuItems]=\"[{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n }]\"\n activedRoute=\"${ROUTES[0].id}\">\n <ui-button actions size=\"small\" applicationTheme=\"${appTheme}\" label=\"PressMe\"></ui-button>\n </ui-navbar>","props":{}},{"name":"WithUserInfoExtra","template":"<ng-template #extraContent>\n User info extra content\n </ng-template>\n <ui-navbar applicationTheme=\"${appTheme}\"\n [userName]=\"'Markus Letterman'\"\n [routes]=\"[\n {\n id: 'assessments',\n title: 'Assessments',\n },\n {\n id: 'results',\n title: 'Results',\n },\n ]\"\n [menuItems]=\"[{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n },{\n id: 'profile',\n icon: 'User-profile',\n text: 'Edit profile info',\n }]\"\n activedRoute=\"${ROUTES[0].id}\"\n email=\"markusletterman@gmail.com\"\n [userInfoExtraTemplateRef]=\"extraContent\">\n </ui-navbar>","props":{}},{"name":"WithMenuContent","template":"<ng-template #menuContent>\n <div class=\"credits-container\">\n <div class=\"credits-header\">\n <span class=\"credits-label\">Credits</span>\n <div class=\"credits-value\">\n <span class=\"credits-count\">360 left</span>\n <ui-icon name=\"Arrow-chevron-right-in-line\" size=\"16\"></ui-icon>\n </div>\n </div>\n <div class=\"credits-progress-bar\">\n <div class=\"credits-progress-fill\"></div>\n </div>\n </div>\n </ng-template>\n <ui-navbar applicationTheme=\"${appTheme}\"\n [menuContentTemplateRef]=\"menuContent\"\n [userName]=\"'Markus Letterman'\"\n [email]=\"'Markus.letterman@email.com'\"\n [routes]=\"[{ id: 'assessments', title: 'Assessments' }]\"\n [menuItems]=\"[\n { id: 'invite', icon: 'User-profile', text: 'Invite team' },\n { id: 'settings', icon: 'User-profile', text: 'Settings' }\n ]\"\n activedRoute=\"assessments\">\n </ui-navbar>\n <style>\n .credits-container {\n margin: 0 16px 16px 16px;\n padding: 16px;\n background: #F5F5F5;\n border-radius: 12px;\n cursor: pointer;\n }\n .credits-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n }\n .credits-label {\n font-weight: 700;\n font-size: 14px;\n font-family: 'ModernGothic', sans-serif;\n color: #242424;\n }\n .credits-value {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n .credits-count {\n font-size: 14px;\n color: #666;\n }\n .credits-progress-bar {\n margin-top: 12px;\n height: 8px;\n background: #E0E0E0;\n border-radius: 4px;\n overflow: hidden;\n }\n .credits-progress-fill {\n width: 70%;\n height: 100%;\n background: linear-gradient(90deg, #E91E8C, #FF69B4);\n border-radius: 4px;\n }\n </style>","props":{}}],"documentation":"The top-level horizontal navigation bar for the TestGorilla product. Provides primary navigation links, the logo, user account menu, and contextual actions (notifications, credits). Adapts responsively across desktop and mobile breakpoints. Used as the persistent chrome on all authenticated product pages.\n\n## When to use\n\n- **Single source of top-level navigation** — don't duplicate nav items elsewhere on the page\n- **Reflect the active route** in the Navbar's active state so users always know where they are\n- **Correct breakpoint variant** → the full horizontal nav renders above `collapseBreakpoint` (1280 by default); at or below it the nav and profile menu move into a right-hand drawer opened from the hamburger. At or below `mobileBreakpoint` (600) the logo also switches to the compact brandmark. Projected `[actions]` content renders at every width.\n\n## When NOT to use\n\n- **Inside a Card or scrollable container** → it must remain fixed at the top of the viewport\n- **Custom navigation items** that aren't part of the defined IA → not without product and design alignment\n- **Candidate-facing flows** → the candidate experience has its own navigation pattern\n\n## How to use\n\nTo use the Navbar component, you need to import the `NavbarComponentModule` in your module:\n\n```typescript\nimport { NavbarComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n NavbarComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-navbar\n [userName]=\"'John Doe'\"\n [routes]=\"routes\"\n [activedRoute]=\"currentRoute\"\n [menuItems]=\"menuItems\"\n (navigateEvent)=\"onNavigate($event)\"\n (menuItemClicked)=\"onMenuItemClicked($event)\"\n (logoutEvent)=\"logout()\"\n>\n</ui-navbar>\n```\n\n### Responsive behaviour\n\n| Viewport | Logo | Navigation + profile menu | Projected `[actions]` |\n| ---------------------- | -------------- | ------------------------- | --------------------- |\n| `> collapseBreakpoint` | 160px wordmark | Inline in the toolbar | Visible |\n| `≤ collapseBreakpoint` | 160px wordmark | Hamburger → right drawer | Visible |\n| `≤ mobileBreakpoint` | 27px brandmark | Hamburger → right drawer | Visible |\n\nBoth thresholds are inputs, so an app with a shorter navigation can collapse later:\n\n```html\n<ui-navbar [collapseBreakpoint]=\"1023\" [mobileBreakpoint]=\"600\"></ui-navbar>\n```\n\nAnything projected into `[actions]` now renders at every width, so give it `min-width: 0` and an\nellipsis rule if it can be wide — the Navbar clips the slot but cannot style your markup.\n\n### Closing the drawer\n\nUse `closeMenu()` to dismiss the drawer after the app handles a `navigateEvent`. It is a no-op when\nthe navbar is not collapsed:\n\n```typescript\nreadonly navbar = viewChild(NavbarComponent);\n\nonNavigate(route: string): void {\n void this.router.navigateByUrl(route);\n this.navbar()?.closeMenu();\n}\n```\n","whenToUse":["**Single source of top-level navigation** — don't duplicate nav items elsewhere on the page","**Reflect the active route** in the Navbar's active state so users always know where they are","**Correct breakpoint variant** → the full horizontal nav renders above `collapseBreakpoint` (1280 by default); at or below it the nav and profile menu move into a right-hand drawer opened from the hamburger. At or below `mobileBreakpoint` (600) the logo also switches to the compact brandmark. Projected `[actions]` content renders at every width."],"whenNotToUse":["**Inside a Card or scrollable container** → it must remain fixed at the top of the viewport","**Custom navigation items** that aren't part of the defined IA → not without product and design alignment","**Candidate-facing flows** → the candidate experience has its own navigation pattern"],"template":"<ui-side-sheet\n class=\"navbar-sheet\"\n [applicationTheme]=\"applicationTheme()\"\n (openChange)=\"onSideSheetOpenChange($event)\"\n position=\"end\"\n>\n <mat-toolbar [attr.theme]=\"applicationTheme()\">\n <div class=\"custom-toolbar row\">\n <div class=\"brand\">\n <ui-logo\n [applicationTheme]=\"applicationTheme()\"\n class=\"logo\"\n (click)=\"navigate('')\"\n [allowResizing]=\"true\"\n [width]=\"isMobile() ? 27 : 160\"\n [type]=\"isMobile() ? logoType.PRIMARY_BRANDMARK_PINK : logoType.DEFAULT\"\n ></ui-logo>\n\n @if (!isCollapsed()) {\n <div class=\"navigation row\">\n @for (route of routes(); track route) {\n <a\n (click)=\"navigate(route.id); $event.preventDefault()\"\n [href]=\"route.id\"\n [attr.data-testid]=\"route.id\"\n data-test-role=\"nav-route\"\n [ngClass]=\"{ active: activeRoutesMap().get(route.id) }\"\n >\n @if (route?.badgeOptions) {\n <ui-badge\n [label]=\"route.badgeOptions!.text ?? ''\"\n [rebrandColor]=\"route.badgeOptions!.color\"\n [variant]=\"route.badgeOptions!.variant ?? 'primary'\"\n [notificationsAmount]=\"route.badgeOptions!.notificationsAmount ?? 0\"\n ></ui-badge>\n }\n <span [attr.data-text]=\"route.title\">{{ route.title }}</span>\n </a>\n }\n </div>\n }\n </div>\n\n <div class=\"actions\">\n <div class=\"custom-actions\">\n <ng-content select=\"[actions]\"></ng-content>\n </div>\n\n @if (isCollapsed()) {\n <ui-button\n data-testid=\"navbar-menu-button\"\n (click)=\"openMobileMenu()\"\n [variant]=\"'text'\"\n [companyColor]=\"'#242424'\"\n [applicationTheme]=\"'light'\"\n [iconName]=\"'Menu-burger-in-line'\"\n [justIcon]=\"true\"\n ></ui-button>\n } @else {\n <button\n mat-button\n class=\"profile-menu\"\n [disableRipple]=\"applicationTheme() !== 'classic'\"\n (menuOpened)=\"onChangeMenuState(true, 'desktop')\"\n (menuClosed)=\"onChangeMenuState(false, 'desktop')\"\n [matMenuTriggerFor]=\"menu\"\n >\n <div class=\"profile-button-content\">\n @if (isAvatar()) {\n <ui-avatar\n data-testid=\"profile-button\"\n [ngClass]=\"{ opened: menu._panelAnimationState !== 'void' }\"\n [applicationTheme]=\"applicationTheme()\"\n [name]=\"userName()\"\n [size]=\"AvatarSize.SMALL_MEDIUM\"\n ></ui-avatar>\n } @else {\n {{ userName() }}\n }\n\n @if (applicationTheme() === 'classic') {\n <mat-icon iconPositionEnd>expand_more</mat-icon>\n } @else {\n @if (!isAvatar()) {\n <ui-icon\n [size]=\"'24'\"\n [name]=\"\n menu._panelAnimationState === 'void' ? 'Arrow-chevron-down-filled' : 'Arrow-chevron-up-filled'\n \"\n [applicationTheme]=\"applicationTheme()\"\n ></ui-icon>\n }\n }\n </div>\n </button>\n }\n </div>\n </div>\n </mat-toolbar>\n\n <ng-content></ng-content>\n\n <mat-menu [class]=\"menuClass()\" #menu=\"matMenu\">\n @if (applicationTheme() === 'classic') {\n @for (e of menuItems(); track e) {\n <button\n mat-menu-item\n (click)=\"clickMenuItem(e.id)\"\n [attr.data-testid]=\"e.id\"\n data-test-role=\"profile-menu-item\"\n >\n <div class=\"menu-item\">\n @if (e?.icon) {\n <ui-icon [name]=\"e.icon\" size=\"24\"></ui-icon>\n }\n {{ e.text }}\n </div>\n </button>\n }\n <button data-testid=\"logout-button\" mat-menu-item (click)=\"logout()\">\n <mat-icon>power_settings_new</mat-icon>\n {{ translationContext + 'LOG_OUT' | uiTranslate | async }}\n </button>\n } @else {\n <ui-mobile-navbar-side-sheet\n [isDesktop]=\"true\"\n [menuItems]=\"menuItems()\"\n [userName]=\"userName()\"\n [routes]=\"routes()\"\n [activedRoute]=\"activedRoute()\"\n [email]=\"email()\"\n [applicationTheme]=\"applicationTheme()\"\n [contentTemplateRef]=\"contentTemplateRef()\"\n [userInfoExtraTemplateRef]=\"userInfoExtraTemplateRef()\"\n [menuContentTemplateRef]=\"menuContentTemplateRef()\"\n (menuItemClicked)=\"clickMenuItem($event)\"\n (logoutEvent)=\"logout()\"\n >\n </ui-mobile-navbar-side-sheet>\n }\n </mat-menu>\n</ui-side-sheet>\n","queryGuide":{"roles":["nav-route","profile-menu-item","menu"],"ariaAttributes":[],"dataTestIds":["navbar-menu-button","profile-button","logout-button"],"materialComponents":["mat-toolbar","mat-icon","mat-menu"],"childUIComponents":["ui-side-sheet","ui-logo","ui-badge","ui-button","ui-avatar","ui-icon","ui-mobile-navbar-side-sheet"],"suggestedQueries":["screen.getByRole('nav-route')","screen.getByRole('profile-menu-item')","screen.getByRole('menu')","screen.getByTestId('navbar-menu-button')","screen.getByTestId('profile-button')","screen.getByTestId('logout-button')"]},"filePath":"components/navbar/navbar.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/8ZDeG0QusLHEYTydC0BWgb/Skills-Profiles-26?node-id=6011-7867&m=dev","lastModified":1786626756405.501},"mobile-navbar-side-sheet":{"name":"MobileNavbarSideSheetComponent","selector":"ui-mobile-navbar-side-sheet","category":"mobile-navbar-side-sheet","description":"MobileNavbarSideSheetComponent component","importPath":"@testgorilla/tgo-ui/components/mobile-navbar-side-sheet","moduleName":"MobileNavbarSideSheetComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"routes","type":"IRoute[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"activedRoute","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"email","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"userName","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"menuItems","type":"MenuItem[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"contentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"userInfoExtraTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"menuContentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"isDesktop","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"navigateEvent","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"menuItemClicked","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"logoutEvent","eventType":"void","alias":null,"description":"","isSignal":true}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<div class=\"mobile-navbar-container\" [ngClass]=\"{ desktop: isDesktop() }\">\n @if (isDesktop()) {\n <ng-container [ngTemplateOutlet]=\"userInfo\"></ng-container>\n }\n\n @if (menuContentTemplateRef()) {\n <ng-container [ngTemplateOutlet]=\"menuContentTemplateRef()!\"></ng-container>\n }\n\n @if (!isDesktop()) {\n <div class=\"routes\">\n @for (route of routes(); track route) {\n <div\n [attr.data-testid]=\"route.id\"\n data-test-role=\"nav-route\"\n tabindex=\"0\"\n (click)=\"navigate(route.id)\"\n (keydown.enter)=\"navigate(route.id)\"\n class=\"route\"\n [ngClass]=\"{ active: activeRoutesMap().get(route.id) }\"\n >\n <span>{{ route.title }}</span>\n </div>\n }\n </div>\n }\n\n <div class=\"settings\">\n @for (e of menuItems(); track e) {\n @if (e) {\n <div class=\"menu-item\">\n <ui-button\n [attr.data-testid]=\"e.id\"\n data-test-role=\"menu-item\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [disabledScaleOnClick]=\"true\"\n [variant]=\"'menuCell'\"\n iconPosition=\"left\"\n [iconName]=\"e.icon\"\n [label]=\"e.text\"\n (buttonClickEvent)=\"clickMenuItem(e.id)\"\n (keydown.Tab)=\"$event.stopPropagation()\"\n (keydown.shift.tab)=\"$event.stopPropagation()\"\n [ariaLabel]=\"e.text + '--' + e.text\"\n ></ui-button>\n </div>\n }\n }\n </div>\n</div>\n\n<div class=\"bottom-content\" [ngClass]=\"{ desktop: isDesktop() }\">\n @if (!isDesktop() && contentTemplateRef()) {\n <ng-container [ngTemplateOutlet]=\"contentTemplateRef()!\"></ng-container>\n }\n @if (isDesktop()) {\n <div class=\"menu-item\">\n <ui-button\n data-testid=\"logout-button\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [disabledScaleOnClick]=\"true\"\n [variant]=\"'menuCell'\"\n iconPosition=\"left\"\n [iconName]=\"'Logout-in-line'\"\n [label]=\"(translationContext + 'LOG_OUT' | uiTranslate | async)!\"\n (buttonClickEvent)=\"logout()\"\n (keydown.Tab)=\"$event.stopPropagation()\"\n (keydown.shift.tab)=\"$event.stopPropagation()\"\n ></ui-button>\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"userInfo\"></ng-container>\n }\n</div>\n\n<ng-template #userInfo>\n <div class=\"user-info\" [ngClass]=\"{ desktop: isDesktop() }\">\n <ui-avatar [size]=\"avatarSize.MEDIUM\" [name]=\"userName()\"></ui-avatar>\n <div class=\"details-container\">\n <div class=\"name-container\">\n <span class=\"user-name dd-privacy-mask\">{{ userName() }}</span>\n <span class=\"user-email dd-privacy-mask\">{{ email() }}</span>\n @if (userInfoExtraTemplateRef()) {\n <ng-container [ngTemplateOutlet]=\"userInfoExtraTemplateRef()!\"></ng-container>\n }\n </div>\n @if (!isDesktop()) {\n <ui-button\n data-testid=\"logout-button\"\n [tooltip]=\"(translationContext + 'LOG_OUT' | uiTranslate | async)!\"\n [iconName]=\"'Logout-in-line'\"\n [applicationTheme]=\"'light'\"\n [variant]=\"'icon-button'\"\n (click)=\"logout()\"\n ></ui-button>\n }\n </div>\n </div>\n</ng-template>\n","queryGuide":{"roles":["nav-route","menu-item"],"ariaAttributes":[],"dataTestIds":["logout-button"],"materialComponents":[],"childUIComponents":["ui-button","ui-avatar"],"suggestedQueries":["screen.getByRole('nav-route')","screen.getByRole('menu-item')","screen.getByTestId('logout-button')"]},"filePath":"components/mobile-navbar-side-sheet/mobile-navbar-side-sheet.component.ts","supportsTheme":true,"lastModified":1786626756405.1255},"overflow-menu":{"name":"OverflowMenuComponent","selector":"ui-overflow-menu","category":"overflow-menu","description":"A triggered dropdown panel containing a contextual list of actions for a specific item — typically surfaced via a three-dot (ellipsis) icon button. Used in data tables, candidate cards, and assessment rows to expose secondary actions (edit, archive, delete, share) without cluttering the primary UI.","importPath":"@testgorilla/tgo-ui/components/overflow-menu","moduleName":"OverflowMenuComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"buttons","type":"OverflowMenuButtonsType[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Menu buttons","isSignal":false},{"name":"iconTrigger","type":"IconName","defaultValue":"'Menu-ellipsis'","required":false,"alias":null,"transform":null,"description":"Menu icon trigger","isSignal":false},{"name":"menuLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Label menu","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"describedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input for description.","isSignal":false},{"name":"contentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Custom template for the button (optional)","isSignal":false},{"name":"buttonVariant","type":"ButtonColor | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Button variant for the overflow menu trigger\nWhen null, uses the default icon rendering","isSignal":false},{"name":"buttonSize","type":"ButtonSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"Button size for the overflow menu trigger","isSignal":false},{"name":"menuConfig","type":"MenuConfig","defaultValue":"DEFAULT_MENU_CONFIG","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"isDynamicMenu","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"withRemovableOption","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[{"name":"selectItem","eventType":"string","alias":null,"description":"","isSignal":false},{"name":"menuOpened","eventType":"void","alias":null,"description":"","isSignal":false},{"name":"menuClosed","eventType":"void","alias":null,"description":"","isSignal":false}],"module":{"name":"OverflowMenuComponentModule","imports":["CommonModule","ButtonComponentModule","IconComponentModule","MatMenuModule","MatButtonModule","MatTooltipModule","EllipseTextDirective","UiTranslatePipe","OverflowMenuComponent"],"exports":["OverflowMenuComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"OverflowMenuButtonsType","value":"{\n icon?: IconName;\n label: string;\n isDestructive?: boolean;\n value: string;\n disabled?: boolean;\n btnTemplate?: TemplateRef<unknown>;\n subMenu?: OverflowMenuButtonsType[] | number;\n action?: number | string;\n describedby?: string;\n ariaLabel?: string;\n tooltipText?: string;\n isTitleButton?: boolean;\n hideTitleButton?: boolean;\n isPremium?: boolean;\n}"},{"name":"ButtonOptions","value":"{\n isTextEllipsed?: boolean;\n isFocused?: boolean;\n isHovered?: boolean;\n}"},{"name":"MenuConfig","value":"{\n xPosition: MenuPositionX;\n yPosition: MenuPositionY;\n overlapTrigger: boolean;\n panelClass: string;\n}"},{"name":"OverflowMenuButtonModify","value":"OverflowMenuButtonsType & ButtonOptions"}],"interfaces":[],"enums":[]},"examples":[{"name":"CustomContent","template":"<ng-template #temp>\n <h4>Custom content</h4>\n </ng-template>\n <div style=\"width: fit-content\">\n <ui-overflow-menu applicationTheme=\"${appTheme}\"\n [contentTemplateRef]=\"temp\"\n [buttons]=\"[\n {\n icon: 'Copy',\n disabled: true,\n label: 'Copy',\n value: 'Copy value',\n },\n {\n icon: 'Delete',\n label: 'Delete',\n value: 'Delete value',\n isDestructive: true,\n }\n ]\"\n >\n </ui-overflow-menu>\n </div>","props":{}}],"documentation":"A triggered dropdown panel containing a contextual list of actions for a specific item — typically surfaced via a three-dot (ellipsis) icon button. Used in data tables, candidate cards, and assessment rows to expose secondary actions (edit, archive, delete, share) without cluttering the primary UI.\n\n## When to use\n\n- **Destructive actions** → group them (delete, reject) at the bottom of the menu, visually separated from non-destructive ones\n- **5 or fewer items** — if you need more, the actions likely belong in a side panel\n- **Verb-led labels** — always label actions with verbs: 'Archive assessment', not 'Archive' alone\n\n## When NOT to use\n\n- **High-frequency actions** → don't use the Overflow Menu as the primary way to access them; if users use it constantly, promote the action to a visible button\n- **Irreversible destructive actions** → don't place them without a confirmation step (Confirm Dialog)\n- **Navigation actions (links)** → don't mix them with in-page actions in the same menu — it confuses the model\n\n## How to use\n\nTo use the Overflow Menu component, you need to import the `OverflowMenuComponentModule` in your module:\n\n```typescript\nimport { OverflowMenuComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n OverflowMenuComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-overflow-menu\n iconTrigger=\"Menu-ellipsis\"\n [buttons]=\"[\n {\n icon: 'Edit',\n label: 'Edit',\n value: 'edit_action'\n },\n {\n icon: 'Delete',\n label: 'Delete',\n value: 'delete_action',\n isDestructive: true\n }\n ]\"\n applicationTheme=\"light\"\n (itemSelected)=\"handleMenuSelection($event)\"\n>\n</ui-overflow-menu>\n```\n\nThe component supports nested sub-menus and various button states including disabled and destructive actions.\n","whenToUse":["**Destructive actions** → group them (delete, reject) at the bottom of the menu, visually separated from non-destructive ones","**5 or fewer items** — if you need more, the actions likely belong in a side panel","**Verb-led labels** — always label actions with verbs: 'Archive assessment', not 'Archive' alone"],"whenNotToUse":["**High-frequency actions** → don't use the Overflow Menu as the primary way to access them; if users use it constantly, promote the action to a visible button","**Irreversible destructive actions** → don't place them without a confirmation step (Confirm Dialog)","**Navigation actions (links)** → don't mix them with in-page actions in the same menu — it confuses the model"],"template":"<div class=\"overflow-menu-container\" [attr.theme]=\"applicationTheme\">\n @if (isMenuRendered()) {\n <button\n #buttonElement\n [ngClass]=\"{\n 'icon-button': !contentTemplateRef && !buttonVariant,\n 'content-template': contentTemplateRef,\n 'no-hover': buttonVariant,\n }\"\n [matMenuTriggerFor]=\"matmenus.first\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-required]=\"ariaRequired\"\n [attr.aria-describedby]=\"'describedby'\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"isMenuOpened()\"\n (menuOpened)=\"onMenuOpened()\"\n (menuClosed)=\"onMenuClosed()\"\n class=\"overflow-menu-opener\"\n [matTooltip]=\"menuLabel\"\n >\n <span [id]=\"'describedby'\" [style.display]=\"'none'\">{{ describedby }}</span>\n @if (buttonVariant) {\n <ui-button\n [variant]=\"buttonVariant\"\n [justIcon]=\"true\"\n [iconName]=\"iconTrigger\"\n [applicationTheme]=\"applicationTheme\"\n [disabled]=\"false\"\n [size]=\"buttonSize\"\n >\n </ui-button>\n } @else if (contentTemplateRef) {\n <ng-container [ngTemplateOutlet]=\"contentTemplateRef\"></ng-container>\n } @else {\n <ui-icon\n [name]=\"iconTrigger\"\n [applicationTheme]=\"applicationTheme\"\n [size]=\"'24'\"\n [color]=\"'rebrand-black'\"\n ></ui-icon>\n }\n </button>\n }\n\n @for (menu of submenus; track submenus[$index]) {\n <mat-menu\n [xPosition]=\"menuConfig.xPosition\"\n [yPosition]=\"menuConfig.yPosition\"\n #matMenu\n [class]=\"'overflow-menu ' + menuConfig.panelClass\"\n [overlapTrigger]=\"menuConfig.overlapTrigger\"\n backdropClass=\"overflow-menu-backdrop\"\n >\n @for (button of menu; track trackByButton(id, button); let id = $index) {\n @if (button.btnTemplate) {\n <!-- Menu option by provided button template-->\n <ng-container\n [ngTemplateOutlet]=\"button.btnTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: button }\"\n ></ng-container>\n } @else if (withRemovableOption) {\n <!-- Simple menu option that can be removed dynamically -->\n <div class=\"menu-option\" [ngClass]=\"{ 'is-destructive': button.isDestructive, disabled: button.disabled }\">\n <span\n [id]=\"'labelledby-' + button.label + id\"\n class=\"label\"\n uiEllipseText\n [refresh]=\"isMenuOpened()\"\n [maxDiffPixels]=\"1\"\n [isMultiline]=\"true\"\n (onChangeTextState)=\"button.isTextEllipsed = $event\"\n >{{ button.label }}\n </span>\n @if (button.icon) {\n <ui-button\n variant=\"icon-button\"\n [applicationTheme]=\"applicationTheme\"\n [iconName]=\"button.icon\"\n [tooltip]=\"button.tooltipText ? button.tooltipText : button.isTextEllipsed ? button.label : ''\"\n [tooltipPosition]=\"'above'\"\n [size]=\"'medium'\"\n (focus)=\"onButtonFocus(button)\"\n (blur)=\"onButtonBlur(button)\"\n (mouseenter)=\"onButtonHover(button, true)\"\n (mouseleave)=\"onButtonHover(button, false)\"\n (click)=\"onSelectItem($event, button.value)\"\n >\n </ui-button>\n }\n </div>\n } @else {\n <!-- Default non-removable menu option -->\n @if (button.isTitleButton && !button.hideTitleButton) {\n <div class=\"menu-title\">{{ button.label }}</div>\n }\n @if (!button.isTitleButton) {\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"getMenu(button.subMenu)\"\n (menuOpened)=\"onSubmenuOpened()\"\n (focus)=\"onButtonFocus(button)\"\n (blur)=\"onButtonBlur(button)\"\n (mouseenter)=\"onButtonHover(button, true)\"\n (mouseleave)=\"onButtonHover(button, false)\"\n [ngClass]=\"{ 'is-destructive': button.isDestructive, disabled: button.disabled }\"\n disableRipple\n (click)=\"onButtonClick(button, $event)\"\n [matTooltip]=\"button.tooltipText ? button.tooltipText : button.isTextEllipsed ? button.label : ''\"\n [attr.aria-labelledby]=\"'labelledby-' + button.label + id\"\n [attr.aria-describedby]=\"'describedby-' + button.label + id\"\n [attr.aria-label]=\"button.ariaLabel || null\"\n role=\"menuitem\"\n matTooltipPosition=\"right\"\n >\n <span [id]=\"'describedby-' + button.label + id\" [style.display]=\"'none'\">\n {{\n button.disabled\n ? ('MENU.MENUITEM_DISABLED' | uiTranslate | async) + ' ' + (button.describedby || '')\n : button.describedby\n }}\n </span>\n @if (button.icon) {\n <ui-icon\n [name]=\"button.icon\"\n [toggleIconStyle]=\"true\"\n [filled]=\"\n button.disabled ? false : button.isHovered ? true : menuItemHasHovered ? false : !!button.isFocused\n \"\n [applicationTheme]=\"applicationTheme\"\n [size]=\"'24'\"\n [color]=\"button.disabled ? 'dark' : button.isDestructive ? 'red' : 'rebrand-black'\"\n ></ui-icon>\n }\n <span\n [id]=\"'labelledby-' + button.label + id\"\n class=\"label\"\n uiEllipseText\n [refresh]=\"isMenuOpened()\"\n [maxDiffPixels]=\"1\"\n [isMultiline]=\"true\"\n (onChangeTextState)=\"button.isTextEllipsed = $event\"\n >{{ button.label }}</span\n >\n @if (button.isPremium) {\n <ui-icon\n [name]=\"'Premium-circle-in-line'\"\n [applicationTheme]=\"applicationTheme\"\n [size]=\"'24'\"\n class=\"premium-icon\"\n ></ui-icon>\n }\n </button>\n }\n }\n }\n\n <ng-content select=\"[menu-content]\"></ng-content>\n </mat-menu>\n }\n</div>\n","queryGuide":{"roles":["menuitem","menu"],"ariaAttributes":["aria-label","aria-required","aria-describedby","aria-haspopup","aria-expanded","aria-labelledby"],"dataTestIds":[],"materialComponents":["mat-menu"],"childUIComponents":["ui-button","ui-icon"],"suggestedQueries":["screen.getByRole('menuitem')","screen.getByRole('menu')","screen.getByLabelText('label text')"]},"filePath":"components/overflow-menu/overflow-menu.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=519-9564","lastModified":1786626756406.1316},"page-header":{"name":"PageHeaderComponent","selector":"ui-page-header","category":"page-header","description":"A standardised page-level header containing a title, optional description, and primary/secondary action buttons. Anchors the top of full-page content areas and gives every page a consistent structure. Used across assessment setup, candidate management, and account settings pages.","importPath":"@testgorilla/tgo-ui/components/page-header","moduleName":"PageHeaderModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"showBackButton","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":"booleanAttribute","description":"Flag to determine whether the \"Back\" button should be displayed.","isSignal":false},{"name":"tooltip","type":"unknown","defaultValue":"'COMMON.BACK'","required":false,"alias":null,"transform":null,"description":"Back button tooltip for mobile variation","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[{"name":"back","eventType":"void","alias":null,"description":"Event triggered when the \"Back\" button is clicked.","isSignal":false}],"module":{"name":"PageHeaderModule","imports":["CommonModule","ButtonComponentModule","UiTranslatePipe","PageHeaderComponent"],"exports":["PageHeaderComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"HeaderWithTemplate","template":"<ui-page-header ${argsToTemplate(mergedArgs)}>\n <div content style=\"display: flex\">\n <ui-avatar style=\"margin-right: 16px\" content size=\"medium\" imageUrl=\"/images/Avatar.png\"></ui-avatar>\n <div style=\"display: flex; flex-direction: column; justify-content: space-between\">\n <div style=\"font-weight: bold; font-size: 20px\">Title</div>\n <div>Text</div>\n </div>\n </div>\n <div right-side style=\"display: flex\">\n <ui-button applicationTheme=\"${appTheme}\" style=\"margin-right: 16px\" variant=\"ghost\" label=\"Button\"></ui-button>\n <ui-button applicationTheme=\"${appTheme}\" label=\"Button\"></ui-button>\n </div>\n </ui-page-header>","props":{}}],"documentation":"A standardised page-level header containing a title, optional description, and primary/secondary action buttons. Anchors the top of full-page content areas and gives every page a consistent structure. Used across assessment setup, candidate management, and account settings pages.\n\n## When to use\n\n- **Always include a title** — the description is optional and only needed when the page purpose isn't self-evident\n- **Right-aligned action slot** for the primary CTA; use the secondary slot sparingly\n- **Full-page views only** — not inside cards or panels, which have their own header patterns\n\n## When NOT to use\n\n- **Navigation or breadcrumbs** → Breadcrumb is a separate component that sits above Page Header, not inside it\n- **More than 2 action buttons** → additional actions belong in an Overflow Menu\n- **Modals or side sheets** → those surfaces have their own header design\n","whenToUse":["**Always include a title** — the description is optional and only needed when the page purpose isn't self-evident","**Right-aligned action slot** for the primary CTA; use the secondary slot sparingly","**Full-page views only** — not inside cards or panels, which have their own header patterns"],"whenNotToUse":["**Navigation or breadcrumbs** → Breadcrumb is a separate component that sits above Page Header, not inside it","**More than 2 action buttons** → additional actions belong in an Overflow Menu","**Modals or side sheets** → those surfaces have their own header design"],"template":"<ng-container>\n <div class=\"page-header-container\">\n <div class=\"page-header-content\">\n @if ((isMobile$ | async) === false) {\n @if (showBackButton) {\n <ui-button\n [applicationTheme]=\"applicationTheme\"\n [tooltip]=\"('COMMON.BACK' | uiTranslate | async)!\"\n (click)=\"onBack()\"\n [ariaLabel]=\"('COMMON.BACK' | uiTranslate | async)!\"\n [variant]=\"'ghost'\"\n [justIcon]=\"true\"\n [iconName]=\"applicationTheme === 'classic' ? 'Arrow_left' : 'Arrow-chevron-left-in-line'\"\n ></ui-button>\n }\n } @else {\n <ui-button\n [applicationTheme]=\"applicationTheme\"\n class=\"mobile-back-btn\"\n [tooltip]=\"('COMMON.BACK' | uiTranslate | async)!\"\n [variant]=\"'icon-button'\"\n [ariaLabel]=\"('COMMON.BACK' | uiTranslate | async)!\"\n (click)=\"onBack()\"\n [justIcon]=\"true\"\n [iconName]=\"applicationTheme === 'classic' ? 'Arrow_left' : 'Arrow-chevron-left-in-line'\"\n ></ui-button>\n }\n\n <div class=\"main-content\" [ngClass]=\"{ 'remove-margin': !showBackButton }\">\n <ng-content select=\"[content]\"></ng-content>\n </div>\n\n <div class=\"right-side\">\n <ng-content select=\"[right-side]\"></ng-content>\n </div>\n </div>\n </div>\n</ng-container>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-button"],"suggestedQueries":[]},"filePath":"components/page-header/page-header.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2714-29606","lastModified":1786626756406.6194},"paginator":{"name":"PaginatorComponent","selector":"ui-paginator","category":"paginator","description":"A pagination control for navigating large datasets split across multiple pages. Displays page numbers, previous/next controls, and optionally a page-size selector. Used in candidate tables, test library results, and assessment lists where datasets exceed a single page.","importPath":"@testgorilla/tgo-ui/components/paginator","moduleName":"PaginatorComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"pageSizeOptions","type":"unknown","defaultValue":"[10, 25, 50]","required":false,"alias":null,"transform":null,"description":"Paginator size options","isSignal":false},{"name":"length","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Data length","isSignal":false},{"name":"pageSize","type":"number","defaultValue":"25","required":false,"alias":null,"transform":null,"description":"Page size","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Disabled","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"currentPage","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Defines the current page","isSignal":false}],"outputs":[{"name":"paginatorChange","eventType":"PageEvent","alias":null,"description":"","isSignal":false}],"module":{"name":"PaginatorComponentModule","imports":["CommonModule","MatPaginatorModule","PaginatorComponent"],"exports":["PaginatorComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A pagination control for navigating large datasets split across multiple pages. Displays page numbers, previous/next controls, and optionally a page-size selector. Used in candidate tables, test library results, and assessment lists where datasets exceed a single page.\n\n## When to use\n\n- **Current page and total count** — always show both so users can orient themselves\n- **Below the table or row-based list** it controls, aligned to the trailing edge\n- **The page-size selector** when users have reason to change how many items they see at once\n\n## When NOT to use\n\n- **Small datasets** → if the data fits fully on one screen, pagination on a 10-item list adds friction with no benefit\n- **Filter changes** → don't reset to page 1 as a side effect when a user applies a filter\n- **A single page of results** → don't show the Paginator if there's only one page\n- **Card grids / roster layouts** → pagination belongs to tables and row-based lists; card grids show the full (capped) result set — for large datasets, switch to a table view instead of paginating cards\n\n## How to use\n\nTo use the Paginator component, you need to import the `PaginatorComponentModule` in your module:\n\n```typescript\nimport { PaginatorComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n PaginatorComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-paginator\n [length]=\"100\"\n [pageSize]=\"10\"\n [pageSizeOptions]=\"[5, 10, 25, 50]\"\n [showFirstLastButtons]=\"true\"\n (page)=\"onPageChange($event)\"\n></ui-paginator>\n```\n","whenToUse":["**Current page and total count** — always show both so users can orient themselves","**Below the table or row-based list** it controls, aligned to the trailing edge","**The page-size selector** when users have reason to change how many items they see at once"],"whenNotToUse":["**Small datasets** → if the data fits fully on one screen, pagination on a 10-item list adds friction with no benefit","**Filter changes** → don't reset to page 1 as a side effect when a user applies a filter","**A single page of results** → don't show the Paginator if there's only one page","**Card grids / roster layouts** → pagination belongs to tables and row-based lists; card grids show the full (capped) result set — for large datasets, switch to a table view instead of paginating cards"],"template":"<mat-paginator\n [disabled]=\"disabled\"\n [length]=\"length\"\n [pageIndex]=\"currentPage ? currentPage : 0\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [attr.theme]=\"applicationTheme\"\n [selectConfig]=\"selectConfig\"\n [color]=\"'accent'\"\n (page)=\"onPaginatorChange($event)\"\n></mat-paginator>\n","queryGuide":{"roles":["navigation"],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-paginator"],"childUIComponents":[],"suggestedQueries":["screen.getByRole('navigation')"]},"filePath":"components/paginator/paginator.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2400-40513","lastModified":1786626756406.9846},"password-criteria":{"name":"PasswordComponent","selector":"ui-password-criteria","category":"password-criteria","description":"PasswordComponent component","importPath":"@testgorilla/tgo-ui/components/password-criteria","moduleName":"PasswordComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"showCriteria","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show criteria for password. Defaults true","isSignal":false},{"name":"required","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Password is required or not","isSignal":false}],"outputs":[{"name":"passwordChange","eventType":"string","alias":null,"description":"","isSignal":false},{"name":"allCriteriaPassed","eventType":"boolean","alias":null,"description":"","isSignal":false}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<div class=\"password-criteria-container\">\n <ui-field\n [applicationTheme]=\"'dark'\"\n [formControl]=\"formControl\"\n [ariaLabel]=\"requiredCriteria?.nativeElement?.innerText ?? ''\"\n [type]=\"'password'\"\n [placeholder]=\"('PASSWORD.PASSWORD_PLACEHOLDER' | uiTranslate | async) + (required ? ' *' : '')\"\n (keydown.enter)=\"showError = true\"\n (focusout)=\"showError = true\"\n ></ui-field>\n\n @if (showCriteria) {\n <div class=\"criteria-container\" #requiredCriteria>\n <div class=\"progress\">\n <ui-progress-bar\n [progress]=\"progressValue === 1 ? 0 : progressValue\"\n [applicationTheme]=\"'dark'\"\n ></ui-progress-bar>\n </div>\n @if (allRequirementsMet) {\n <span style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">{{\n 'FIELD.ALL_PASSWORD_REQ_PASSED' | uiTranslate | async\n }}</span>\n }\n @for (criterion of criteria; track criterion) {\n <div\n class=\"criterion\"\n [attr.aria-live]=\"!allRequirementsMet ? 'polite' : ''\"\n [ngClass]=\"{ passed: criteriaPassed[criterion.key], error: !criteriaPassed[criterion.key] && showError }\"\n >\n <ui-icon\n [size]=\"'24'\"\n [color]=\"'rebrand-black'\"\n [name]=\"!criteriaPassed[criterion.key] && showError ? 'Info-in-line' : 'Check-round-in-line'\"\n applicationTheme=\"dark\"\n ></ui-icon>\n <span>{{ criterion.translateKey | uiTranslate | async }}</span>\n <span style=\"position: absolute; left: -9999px\">{{\n (criteriaPassed[criterion.key] ? 'COMMON.PASSED' : 'COMMON.FAILED') | uiTranslate | async\n }}</span>\n </div>\n }\n </div>\n }\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-live"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-field","ui-progress-bar","ui-icon"],"suggestedQueries":[]},"filePath":"components/password-criteria/password-criteria.component.ts","supportsTheme":false,"lastModified":1786626756407.3909},"password-strength":{"name":"PasswordStrengthComponent","selector":"ui-password-strength","category":"password-strength","description":"A visual strength meter that evaluates password quality in real time as the user types. Shows a segmented bar and label (e.g. 'Weak', 'Fair', 'Strong') to guide users toward setting a secure password. Used exclusively alongside the Password input in registration and password-change flows.","importPath":"@testgorilla/tgo-ui/components/password-strength","moduleName":"PasswordStrengthComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"password","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The password that needs to be checked","isSignal":false}],"outputs":[{"name":"validationCheck","eventType":"boolean","alias":null,"description":"Event that outputs validation check result","isSignal":false}],"module":{"name":"PasswordStrengthComponentModule","imports":["CommonModule","IconComponentModule","ProgressBarComponentModule","UiTranslatePipe","PasswordStrengthComponent"],"exports":["PasswordStrengthComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A visual strength meter that evaluates password quality in real time as the user types. Shows a segmented bar and label (e.g. 'Weak', 'Fair', 'Strong') to guide users toward setting a secure password. Used exclusively alongside the Password input in registration and password-change flows.\n\n## When to use\n\n- **Position directly below the password field** it evaluates so the feedback is clearly tied to its input\n- **Update on every keystroke** so feedback is immediate as the user types\n- **Pair the label text with the bar** — colour alone is not sufficient for users with colour vision deficiency\n\n## When NOT to use\n\n- **Blocking submit for 'Weak' passwords** → don't disable submission without explaining the minimum requirements\n- **Login forms** → it only belongs on password creation, not on sign-in\n- **Long bar transitions** → don't animate the strength bar slowly; feedback should feel instant\n\n## How to use\n\nTo use the Password Strength component, you need to import the `PasswordStrengthComponentModule` in your module:\n\n```typescript\nimport { PasswordStrengthComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n PasswordStrengthComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-password-strength [password]=\"myPassword\" applicationTheme=\"light\" (strengthChange)=\"handleStrengthChange($event)\">\n</ui-password-strength>\n```\n\nThe component provides visual feedback on password strength based on defined criteria.\n","whenToUse":["**Position directly below the password field** it evaluates so the feedback is clearly tied to its input","**Update on every keystroke** so feedback is immediate as the user types","**Pair the label text with the bar** — colour alone is not sufficient for users with colour vision deficiency"],"whenNotToUse":["**Blocking submit for 'Weak' passwords** → don't disable submission without explaining the minimum requirements","**Login forms** → it only belongs on password creation, not on sign-in","**Long bar transitions** → don't animate the strength bar slowly; feedback should feel instant"],"template":"<div class=\"criteria-container\">\n <div class=\"progress\">\n <ui-progress-bar\n [progress]=\"progressValue === 1 ? 0 : progressValue\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-progress-bar>\n </div>\n\n @for (criterion of criteria; track criterion) {\n <div\n class=\"criterion\"\n [ngClass]=\"{ passed: criteriaPassed[criterion.key], error: !criteriaPassed[criterion.key] && showError }\"\n >\n <ui-icon\n [size]=\"'24'\"\n [color]=\"'rebrand-black'\"\n [name]=\"!criteriaPassed[criterion.key] ? infoIcon : checkIcon\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-icon>\n <span>{{ criterion.translateKey | uiTranslate | async }}</span>\n </div>\n }\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-progress-bar","ui-icon"],"suggestedQueries":[]},"filePath":"components/password-strength/password-strength.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=23270-47639","lastModified":1786626756407.8633},"phone-input":{"name":"PhoneInputComponent","selector":"ui-phone-input","category":"phone-input","description":"A specialised input for international phone number entry, with a country code selector (flag + dial code) combined with a number field. Handles formatting and validation per locale. Used in candidate profile setup and hiring manager account settings where phone numbers are required.","importPath":"@testgorilla/tgo-ui/components/phone-input","moduleName":"PhoneInputComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"disabled","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines if input is disabled","isSignal":false},{"name":"required","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input is required or not","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator of the phone input width","isSignal":false},{"name":"hideBuiltInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide in build errors for Reactive Forms","isSignal":false},{"name":"hideLabelInErrors","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Hide label in errors","isSignal":false},{"name":"value","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Phone number","isSignal":false},{"name":"countryList","type":"UICountry[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Country list for select","isSignal":false},{"name":"placeholder","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Placeholder input","isSignal":false},{"name":"errors","type":"string[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input field errors","isSignal":false}],"outputs":[],"module":{"name":"PhoneInputComponentModule","imports":["CommonModule","MatProgressSpinnerModule","ReactiveFormsModule","MatSelectModule","MatIconModule","MatInputModule","NgxMatSelectSearchModule","IconComponentModule","SpinnerComponentModule","UiTranslatePipe","ValidationErrorModule","HasValidationErrorPipe","PhoneInputComponent","JoinStringsPipe"],"exports":["PhoneInputComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"UICountry","properties":[{"name":"name","type":"string","optional":false},{"name":"alpha2Code","type":"string","optional":false},{"name":"callingCode","type":"string","optional":false}]}],"enums":[]},"examples":[],"documentation":"A specialised input for international phone number entry, with a country code selector (flag + dial code) combined with a number field. Handles formatting and validation per locale. Used in candidate profile setup and hiring manager account settings where phone numbers are required.\n\n## When to use\n\n- **Default the country selector** to the most likely locale for the user, based on their account region if available\n- **Validate the number format on blur** so users can finish typing before seeing an error\n- **Show a clear error state** if the number format doesn't match the selected country code\n\n## When NOT to use\n\n- **A plain Text Input for phone numbers** → Phone Input handles international formatting that a generic field cannot\n- **A phone number that isn't essential** → it's a high-friction field that reduces form completion\n- **Silent cleanup of input** → don't strip leading zeros or country codes silently; validate explicitly\n\n## How to use\n\nTo use the Phone Input component, you need to import the `PhoneInputComponentModule` in your module:\n\n```typescript\nimport { PhoneInputComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n PhoneInputComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-phone-input\n [countryList]=\"countryList\"\n [(value)]=\"phoneNumber\"\n applicationTheme=\"light\"\n (valueChange)=\"onPhoneNumberChange($event)\"\n>\n</ui-phone-input>\n```\n\nThe component allows users to input phone numbers with country code selection.\n","whenToUse":["**Default the country selector** to the most likely locale for the user, based on their account region if available","**Validate the number format on blur** so users can finish typing before seeing an error","**Show a clear error state** if the number format doesn't match the selected country code"],"whenNotToUse":["**A plain Text Input for phone numbers** → Phone Input handles international formatting that a generic field cannot","**A phone number that isn't essential** → it's a high-friction field that reduces form completion","**Silent cleanup of input** → don't strip leading zeros or country codes silently; validate explicitly"],"template":"<mat-form-field\n [formGroup]=\"form\"\n appearance=\"outline\"\n [color]=\"errorsLength || (ngControl?.errors && ngControl?.touched) ? 'warn' : 'accent'\"\n [ngClass]=\"{ disabled: form.disabled || disabled, 'full-width': fullWidth }\"\n>\n <mat-label\n >{{ 'COMMON.PHONE_NUMBER' | uiTranslate | async }}\n @if (required) {\n <span>*</span>\n }\n </mat-label>\n\n <div matPrefix class=\"country\">\n @if (selectedCountry) {\n <button class=\"selected-flag\" matPrefix>\n <img\n [height]=\"20\"\n [width]=\"24\"\n [attr.src]=\"countryFlagSrc + selectedCountry?.alpha2Code?.toLowerCase() + '.svg'\"\n [alt]=\"'Country flag ' + selectedCountry?.alpha2Code?.toLowerCase()\"\n />\n </button>\n }\n <mat-select\n #select\n (selectionChange)=\"onOptionsSelected($event.value)\"\n [value]=\"selectedCountry\"\n [disableOptionCentering]=\"true\"\n [panelClass]=\"'phone-number-select' + ' ' + applicationTheme\"\n [ngClass]=\"{ opened: select.panelOpen }\"\n >\n <mat-select-trigger>{{ selectedCountry?.callingCode }}</mat-select-trigger>\n <mat-option>\n <ngx-mat-select-search\n [formControl]=\"selectFilterCtrl\"\n ngDefaultControl\n placeholderLabel=\"\"\n noEntriesFoundLabel=\"\"\n >\n <ui-icon ngxMatSelectSearchClear size=\"24\" [name]=\"'Close-in-line'\"></ui-icon>\n </ngx-mat-select-search>\n </mat-option>\n @for (country of countries$ | async; track filteredCountryTrackByMethod($index, country)) {\n <mat-option [value]=\"country\">\n <div class=\"country-option\">\n <img\n [height]=\"20\"\n [width]=\"24\"\n [attr.src]=\"countryFlagSrc + country?.alpha2Code?.toLowerCase()! + '.svg'\"\n [alt]=\"'Country flag ' + country?.alpha2Code?.toLowerCase()!\"\n />\n <small>{{ $any([country?.name, '(' + country?.callingCode + ')']) | joinStrings: ' ' }}</small>\n </div>\n </mat-option>\n }\n </mat-select>\n </div>\n\n <input\n data-testid=\"phone-input-number\"\n (input)=\"onInput(form.get('phone_number')!.value)\"\n formControlName=\"phone_number\"\n (click)=\"disableClick($event)\"\n [ngStyle]=\"{ opacity: disabled ? 0.5 : 1 }\"\n [required]=\"required\"\n matInput\n type=\"text\"\n />\n\n @if (errorsLength || (ngControl?.errors | hasValidationError)) {\n <mat-hint class=\"error\">\n @if (errorsLength) {\n @for (error of _errors; track trackByFn($index, error)) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span [innerHTML]=\"error\"></span>\n </div>\n }\n }\n @if (ngControl && !hideBuiltInErrors) {\n <ui-validation-error\n [ngControl]=\"ngControl\"\n [label]=\"hideLabelInErrors ? null : ('COMMON.PHONE_NUMBER' | uiTranslate | async)\"\n ></ui-validation-error>\n }\n </mat-hint>\n }\n</mat-form-field>\n","queryGuide":{"roles":["combobox","option"],"ariaAttributes":[],"dataTestIds":["phone-input-number"],"materialComponents":["mat-form-field","mat-label","mat-select","mat-select-trigger","mat-option","mat-hint"],"childUIComponents":["ui-icon","ui-validation-error"],"suggestedQueries":["screen.getByRole('combobox')","screen.getByRole('option')","// Open dropdown: await userEvent.click(screen.getByRole('combobox'))","// Select option: await userEvent.click(await screen.findByText('Option'))","screen.getByTestId('phone-input-number')"]},"filePath":"components/phone-input/phone-input.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=3219-14535","lastModified":1786626756408.6343},"popover":{"name":"PopoverComponent","selector":"ui-popover","category":"popover","description":"PopoverComponent component","importPath":"@testgorilla/tgo-ui/components/popover","moduleName":"PopoverComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"templateRef","type":"TemplateRef<unknown> | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"ctx","type":"Record<string, unknown>","defaultValue":"{}","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"popoverCard","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"@if (templateRef()) {\n @if (popoverCard()) {\n <ui-card>\n <ng-container *ngTemplateOutlet=\"templateRef(); context: ctx()\"></ng-container>\n </ui-card>\n } @else {\n <ng-container *ngTemplateOutlet=\"templateRef(); context: ctx()\"></ng-container>\n }\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-card"],"suggestedQueries":[]},"filePath":"components/popover/popover.component.ts","supportsTheme":false,"lastModified":1786626756408.9253},"progress-bar":{"name":"ProgressBarComponent","selector":"ui-progress-bar","category":"progress-bar","description":"A horizontal bar showing completion or loading progress. Supports four modes: determinate (known percentage), indeterminate (unknown duration), query (pre-loading animation), and buffer (buffered progress with a secondary track). Used in assessment loading, file upload progress, and onboarding completion tracking.","importPath":"@testgorilla/tgo-ui/components/progress-bar","moduleName":"ProgressBarComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"companyColor","type":"string | null","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Color of the progress bar.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"progress","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Progress value of the progress bar.\nDefaults to zero.","isSignal":false},{"name":"mode","type":"ProgressBarMode","defaultValue":"'determinate'","required":false,"alias":null,"transform":null,"description":"Mode of the progress bar.\nMode must be one of these values: determinate, indeterminate, buffer, query.\nDefaults to 'determinate'.","isSignal":false},{"name":"buffer","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Buffer value of the progress bar.\nDefaults to zero.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaDescribedBy","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"showPercentage","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Shows the percentage amount","isSignal":false}],"outputs":[],"module":{"name":"ProgressBarComponentModule","imports":["CommonModule","MatProgressBarModule","UiTranslatePipe","ProgressBarComponent"],"exports":["ProgressBarComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A horizontal bar showing completion or loading progress. Supports four modes: determinate (known percentage), indeterminate (unknown duration), query (pre-loading animation), and buffer (buffered progress with a secondary track). Used in assessment loading, file upload progress, and onboarding completion tracking.\n\n## When to use\n\n- **Known percentage** → use determinate mode whenever the percentage is known, giving users a concrete sense of how far along they are\n- **Genuinely unknown duration** → use indeterminate for operations where duration can't be predicted (e.g. server-side processing after submission)\n- **Upload flows** → pair determinate bars with a text label ('Uploading… 45%') so progress stays legible\n\n## When NOT to use\n\n- **Multi-step wizard flows** → use the Stepper component instead\n- **Indefinite waits** → don't leave an indeterminate bar running forever without a timeout or error state\n- **Binary states (done / not done)** → use a Spinner or status Badge instead\n\n## How to use\n\nTo use the Progress Bar component, you need to import the `ProgressBarComponentModule` in your module:\n\n```typescript\nimport { ProgressBarComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ProgressBarComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-progress-bar mode=\"determinate\" [progress]=\"40\" applicationTheme=\"light\"> </ui-progress-bar>\n```\n\nThe component supports several modes:\n\n- **determinate**: Shows progress as a percentage\n- **indeterminate**: Shows an animation for unknown progress\n- **query**: Similar to indeterminate, but with a different animation\n- **buffer**: Shows both progress and a buffer progress\n","whenToUse":["**Known percentage** → use determinate mode whenever the percentage is known, giving users a concrete sense of how far along they are","**Genuinely unknown duration** → use indeterminate for operations where duration can't be predicted (e.g. server-side processing after submission)","**Upload flows** → pair determinate bars with a text label ('Uploading… 45%') so progress stays legible"],"whenNotToUse":["**Multi-step wizard flows** → use the Stepper component instead","**Indefinite waits** → don't leave an indeterminate bar running forever without a timeout or error state","**Binary states (done / not done)** → use a Spinner or status Badge instead"],"template":"@if (showPercentage) {\n <div class=\"percentage\">{{ progress }}%</div>\n}\n\n<mat-progress-bar\n [mode]=\"mode\"\n [value]=\"progress\"\n [bufferValue]=\"buffer\"\n title=\"progress-bar\"\n class=\"progress-bar\"\n [attr.theme]=\"applicationTheme\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-live]=\"'polite'\"\n [attr.aria-describedby]=\"'describedBy'\"\n>\n</mat-progress-bar>\n@if (mode === 'indeterminate') {\n <div style=\"position: absolute; left: -9999px\" role=\"alert\">\n {{ 'COMMON.LOADING' | uiTranslate | async }}\n </div>\n}\n<div style=\"display: none\" id=\"describedBy\">\n {{ ariaDescribedBy }}\n</div>\n","queryGuide":{"roles":["alert","progressbar"],"ariaAttributes":["aria-label","aria-live","aria-describedby"],"dataTestIds":[],"materialComponents":["mat-progress-bar"],"childUIComponents":[],"suggestedQueries":["screen.getByRole('alert')","screen.getByRole('progressbar')","screen.getByLabelText('label text')"]},"filePath":"components/progress-bar/progress-bar.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2412-41340","lastModified":1786626756409.651},"prompt":{"name":"PromptComponent","selector":"ui-prompt","category":"prompt","description":"An AI text input field designed for natural language queries and instructions within AI-powered product surfaces. Provides a styled, multi-line input with send controls tuned for the AI interview and AI-assisted assessment creation contexts. Communicates 'AI interaction' through its visual design.","importPath":"@testgorilla/tgo-ui/components/prompt","moduleName":"PromptModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"tags","type":"PromptTag[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"actionTag","type":"PromptActionTag | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxCharacters","type":"number","defaultValue":"255","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"supportedFileTypes","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"autoClear","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"showSendButton","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"sendButtonDisabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"enableFileUpload","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Placeholder for the prompt.","isSignal":true},{"name":"errorMessage","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Custom error message displayed below the prompt.","isSignal":true},{"name":"dropdownItems","type":"PromptDropdownItem[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"Items for the built-in dropdown; opens on focus, filters by label as the user types.","isSignal":true},{"name":"dropdownHeading","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"showDropdownClose","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"promptData","eventType":"PromptData","alias":null,"description":"","isSignal":true},{"name":"actionTagFileSelected","eventType":"File","alias":null,"description":"","isSignal":true},{"name":"dropdownItemSelected","eventType":"PromptDropdownItem","alias":null,"description":"","isSignal":true},{"name":"dropdownClosed","eventType":"void","alias":null,"description":"","isSignal":true}],"module":{"name":"PromptModule","imports":["CommonModule","FieldComponentModule","ButtonComponentModule","OverflowMenuComponentModule","TagComponentModule","BadgeComponentModule","CdkOverlayOrigin","CdkConnectedOverlay","CardComponentModule","CdkTextareaAutosize","TooltipComponentModule","MatTooltip","UiTranslatePipe","IconComponentModule","SkeletonComponent","PromptComponent"],"exports":["PromptComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"PromptTag","properties":[{"name":"id","type":"number","optional":false},{"name":"label","type":"string","optional":false},{"name":"autocomplete","type":"string","optional":false}]},{"name":"PromptData","properties":[{"name":"text","type":"string","optional":true},{"name":"files","type":"File[]","optional":true}]},{"name":"PromptActionTag","properties":[{"name":"label","type":"string","optional":false},{"name":"icon","type":"IconName","optional":true},{"name":"ariaLabel","type":"string","optional":true},{"name":"disabled","type":"boolean","optional":true},{"name":"acceptedFileTypes","type":"string","optional":true}]},{"name":"PromptDropdownItem","properties":[{"name":"id","type":"number","optional":false},{"name":"label","type":"string","optional":false},{"name":"icon","type":"IconName","optional":true}]}],"enums":[]},"examples":[{"name":"Default","template":"<div class=\"row\">\n <ui-prompt></ui-prompt>\n </div>","props":{}},{"name":"CustomPlaceholder","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"placeholder":"'Custom placeholder'"}},{"name":"CustomErrorMessage","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"errorMessage":"'This is a custom error message'"}},{"name":"Disabled","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"disabled":"true","enableFileUpload":"true","showSendButton":"true","tags":"[","id":"1, label: 'Frontend Developer', autocomplete: 'builds UI'"}},{"name":"WithoutTags","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"enableFileUpload":"true","showSendButton":"true"}},{"name":"LessThan5Tags","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"tags":"[","id":"1, label: 'Frontend Developer', autocomplete: 'builds UI'"}},{"name":"WithViewMoreDropdown","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"tags":"[","id":"1, label: 'Frontend Developer', autocomplete: 'builds UI'"}},{"name":"WithAllOptions","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"enableFileUpload":"true","showSendButton":"true","tags":"[","id":"1, label: 'Frontend Developer', autocomplete: 'builds UI'"}},{"name":"WithActionTag","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"actionTag":"{ label: 'Upload job description', icon: 'Plus'"}},{"name":"Loading","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"loading":"true","enableFileUpload":"true","showSendButton":"true","actionTag":"{ label: 'Job description', icon: 'Plus'"}},{"name":"WithActionTagAndSuggestionTags","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"actionTag":"{ label: 'Upload job description', icon: 'Plus'"}},{"name":"WithDropdownItems","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)}></ui-prompt>\n </div>","props":{"placeholder":"'Ask me anything…'","dropdownHeading":"'Recent searches'","showDropdownClose":"true","dropdownItems":"[","id":"1","label":"'Looking for a Senior Product designer, with Figma skills and based in London area.'","icon":"'History-in-line'"}},{"name":"EnterToSend","template":"<div class=\"row\">\n <ui-prompt ${argsToTemplate(args)} (promptData)=\"sent = $event.text\"></ui-prompt>\n <p class=\"sent\">Press Enter to send, Shift+Enter for a new line. Last sent: {{ sent || '—' }}</p>\n </div>","props":{"showSendButton":"true","autoClear":"true"}}],"documentation":"An AI text input field designed for natural language queries and instructions within AI-powered product surfaces. Provides a styled, multi-line input with send controls tuned for the AI interview and AI-assisted assessment creation contexts. Communicates 'AI interaction' through its visual design.\n\n## When to use\n\n- **AI interaction surfaces only** — assessment prompt entry, interview question generation, and AI chat\n- **Meaningful length limits** — always show a character or word count when prompt length is capped\n- **Paired with the AI Caveat component** — add it to the output surface that follows prompt submission\n\n## When NOT to use\n\n- **Standard form inputs** → for name, email, or text answers use the Field component instead\n- **Empty prompts** → validate before triggering an AI call, never submit an empty prompt\n- **Search fields** → Prompt signals AI interaction, not information retrieval\n\n## How to use\n\nTo use the Prompt component, you need to import the `PromptComponentModule` in your module:\n\n```typescript\nimport { PromptComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n PromptComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-prompt\n [tags]=\"[{ id: 1, label: 'Frontend Developer', autocomplete: 'builds UI' },\n { id: 2, label: 'Backend Developer', autocomplete: 'handles server logic' }]\"\n [showSendButton]=\"true\"\n [enableFileUpload]=\"true\"\n [autoClear]=\"false\"\n>\n</ui-prompt>\n```\n\n## Action Tag\n\nConfigure the `actionTag` input to render an Actionable Tag in the footer Left slot. Pressing the tag opens a native file picker; the chosen `File` is emitted via `(actionTagFileSelected)`. The consumer decides what to do with the file — parse it, upload it, write the result back to the prompt via the form control, etc. The library does **not** parse files or attach them to `PromptData.files`.\n\n```ts\nimport { PromptActionTag } from '@testgorilla/tgo-ui/components/prompt';\n\nreadonly jdUploadTag: PromptActionTag = {\n label: 'Upload job description',\n icon: 'Plus',\n acceptedFileTypes: '.pdf,.docx,.txt', // passed to the file input's accept attribute\n};\n```\n\n```html\n<ui-prompt [formControl]=\"promptControl\" [actionTag]=\"jdUploadTag\" (actionTagFileSelected)=\"onJdSelected($event)\">\n</ui-prompt>\n```\n\n```ts\nonJdSelected(file: File) {\n // Parse the file (backend call, pdf-parse, mammoth, etc.) and write the text back:\n this.jdService.extract(file).subscribe(text => {\n this.promptControl.setValue({ text, files: this.promptControl.value?.files ?? [] });\n });\n}\n```\n\n### When to use vs `enableFileUpload`\n\n- Use `enableFileUpload` when you want the user to **attach** files alongside text (each file is carried through as a raw `File` object in `PromptData.files`, with a badge + overflow menu listing them).\n- Use `actionTag` when the file is a one-shot input — your code reads it, does something with it, and writes the result back into the prompt yourself.\n\nThe two modes are mutually exclusive at render time: if `actionTag` is set, the Plus button + file-list overflow menu are not rendered, even if `enableFileUpload` is also true.\n\n## Loading state\n\nSet `[loading]=\"true\"` while the consumer is preparing the data the user needs before they can prompt (e.g. fetching defaults, hydrating context, restoring a draft). The textarea and Sparkle icon are swapped for skeleton placeholders, every interactive control in the footer is disabled, and the container is marked `aria-busy=\"true\"` so assistive tech announces the wait.\n\n```html\n<ui-prompt [formControl]=\"promptControl\" [loading]=\"isPreparing()\" [showSendButton]=\"true\" [enableFileUpload]=\"true\">\n</ui-prompt>\n```\n\nBehavior while `loading` is `true`:\n\n- The textarea is unmounted — any pending autocomplete is discarded.\n- Action tag, file upload, suggestion tags, scroll buttons, view-more dropdown, and the send button are all disabled.\n- Public methods (`sendPromptData`, `selectTag`, `toggleDropdown`, `onActionTagPress`, etc.) are no-ops, so programmatic dispatch from a parent is also blocked.\n\n`loading` composes with `disabled`: the prompt is locked when either is `true`. `loading` does not change the form control's value; reset or pre-populate the control yourself when the loading work completes.\n\n## Disabling\n\nThe prompt accepts two equivalent disable signals:\n\n- `[disabled]=\"true\"` — direct input binding.\n- `formControl.disable()` via the `ControlValueAccessor` integration.\n\nPick one source of truth per usage. If both are wired simultaneously, the last write wins.\n\n## Keyboard\n\n| Key | Behavior |\n| --- | --- |\n| `Enter` | Submits the prompt, emitting `promptData` — the same path as the Send button. |\n| `Shift+Enter` | Inserts a newline. `Ctrl`, `Cmd` and `Alt` + `Enter` also insert a newline. |\n| `Tab` | Accepts the autocomplete suggestion when one is showing. |\n| `Escape` | Closes the items dropdown and emits `dropdownClosed`. |\n\n`Enter` submits whenever submission is permitted — the prompt is neither `disabled` nor `loading`,\n`sendButtonDisabled` is false, and there is non-whitespace text or at least one file. This is the\nsame condition that drives the Send button's disabled state, so the two can never disagree.\n\nThe shortcut does **not** require `showSendButton`. With no Send button rendered, `Enter` is the\nonly send affordance.\n\nWhitespace-only text never submits, by keyboard or by button. Text that merely has leading or\ntrailing whitespace does submit, and `PromptData.text` carries it unmodified — the component never\nrewrites what the user typed.\n\n`Enter` pressed to commit an IME candidate (Japanese, Chinese, Korean input) does not submit.\n","whenToUse":["**AI interaction surfaces only** — assessment prompt entry, interview question generation, and AI chat","**Meaningful length limits** — always show a character or word count when prompt length is capped","**Paired with the AI Caveat component** — add it to the output surface that follows prompt submission"],"whenNotToUse":["**Standard form inputs** → for name, email, or text answers use the Field component instead","**Empty prompts** → validate before triggering an AI call, never submit an empty prompt","**Search fields** → Prompt signals AI interaction, not information retrieval"],"template":"<ng-container>\n <div\n class=\"prompt-container\"\n [class.has-error]=\"!!errorMessage()\"\n [class.disabled]=\"isLocked()\"\n [attr.aria-disabled]=\"isLocked()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n cdkOverlayOrigin\n #promptOrigin=\"cdkOverlayOrigin\"\n #promptContainerRef\n [ngClass]=\"{ 'prompt-container-focused': isHighlighted() && !loading() }\"\n >\n @if (loading()) {\n <div class=\"prompt-loading\" aria-hidden=\"true\">\n <ui-skeleton [theme]=\"{ width: '100%', height: '20px', margin: '0' }\"></ui-skeleton>\n <ui-skeleton [theme]=\"{ width: '30%', height: '20px', margin: '0' }\"></ui-skeleton>\n </div>\n }\n <div class=\"prompt-autocomplete\" [hidden]=\"loading()\">\n <textarea tabindex=\"-1\" class=\"ghost\" id=\"autocomplete\" [value]=\"autocompleteSuggestion()\" readonly></textarea>\n\n @let showIcon = !isFieldFocused() && !textarea.value;\n @if (showIcon) {\n <ui-icon\n class=\"prompt-icon\"\n [color]=\"'inherit'\"\n [class.hovered]=\"isFieldHovered()\"\n [name]=\"'Sparkle-in-line'\"\n ></ui-icon>\n }\n <!-- `keydown.enter` matches unmodified Enter only; Shift+Enter resolves to `shift.enter`\n and falls through to a newline. Do not add a shiftKey check. -->\n <ui-field\n #textarea\n class=\"prompt-text\"\n [class.show-icon]=\"showIcon\"\n role=\"input\"\n [showBottomContent]=\"false\"\n [borderless]=\"true\"\n [type]=\"'multi-line'\"\n [autosizableTextarea]=\"true\"\n [maxCharacters]=\"maxCharacters()\"\n [placeholder]=\"showIcon ? placeholder() || ('PROMPT.ASK_ANYTHING' | uiTranslate | async)! : ''\"\n [value]=\"promptTextValue() || ''\"\n [hasTextAreaCounter]=\"false\"\n [disabled]=\"disabled\"\n (mouseenter)=\"isFieldHovered.set(true)\"\n (mouseleave)=\"isFieldHovered.set(false)\"\n (focusin)=\"isFieldFocused.set(true)\"\n (fieldBlur)=\"onFieldBlur()\"\n (keydown.tab)=\"onTextareaTab($event)\"\n (keydown.enter)=\"onTextareaEnter($event)\"\n (keydown.escape)=\"onTextareaEscape($event)\"\n ></ui-field>\n </div>\n <div class=\"prompt-footer flex-container\">\n <div class=\"flex-container\">\n @if (actionTag(); as at) {\n <ui-tag\n [label]=\"at.label\"\n [icon]=\"at.icon ?? 'Plus'\"\n [ariaLabel]=\"at.ariaLabel ?? at.label\"\n [isDisabled]=\"isLocked() || !!at.disabled\"\n (press)=\"onActionTagPress()\"\n ></ui-tag>\n } @else if (enableFileUpload()) {\n <ui-button\n [variant]=\"'icon-button'\"\n [tooltip]=\"('PROMPT.ADD_FILES' | uiTranslate | async)!\"\n [tooltipPosition]=\"'above'\"\n [ariaLabel]=\"('PROMPT.ADD_FILES' | uiTranslate | async)!\"\n [iconName]=\"'Plus'\"\n [disabled]=\"isLocked()\"\n (click)=\"fileDrop()?.nativeElement?.click()\"\n ></ui-button>\n\n @if (promptFilesList().length) {\n <div class=\"menu\">\n <ui-badge\n [notificationsAmount]=\"promptFilesList().length\"\n [variant]=\"'notification'\"\n class=\"menu-badge\"\n ></ui-badge>\n <ui-overflow-menu\n [matTooltip]=\"('PROMPT.FILES' | uiTranslate | async)!\"\n [matTooltipPosition]=\"'above'\"\n [buttonSize]=\"'medium'\"\n [iconTrigger]=\"'Document'\"\n [buttonVariant]=\"'ghost'\"\n [buttons]=\"fileListMenuConfig()\"\n [isDynamicMenu]=\"true\"\n [withRemovableOption]=\"true\"\n (selectItem)=\"selectedMenuOption($event)\"\n ></ui-overflow-menu>\n </div>\n }\n }\n\n @if (actionTag() || enableFileUpload()) {\n <input\n data-testid=\"prompt-file-input\"\n [tabIndex]=\"-1\"\n class=\"form-control\"\n #fileDropRef\n type=\"file\"\n id=\"file-upload\"\n [accept]=\"fileInputAccept()\"\n [disabled]=\"isLocked()\"\n (change)=\"onFileSelected($event)\"\n />\n }\n </div>\n\n @if (tags()?.length && !promptTextValue()) {\n <div class=\"flex-container carousel\">\n <div class=\"tag-container flex-container\" role=\"list\" #tagsScrollContainer>\n @for (tag of tagsVisible(); track tag) {\n <ui-tag\n [label]=\"tag.label\"\n role=\"listitem\"\n [ariaLabel]=\"tag.label\"\n [isDisabled]=\"isLocked()\"\n (click)=\"selectTag(tag, $event)\"\n (keyup.enter)=\"selectTag(tag, $event)\"\n (keyup.space)=\"selectTag(tag, $event)\"\n ></ui-tag>\n }\n\n @if (tagsInDropdown().length) {\n <ui-button\n class=\"view-more\"\n variant=\"text-inline\"\n [label]=\"('PROMPT.VIEW_MORE' | uiTranslate | async)!\"\n [ariaLabel]=\"('PROMPT.VIEW_MORE' | uiTranslate | async)!\"\n [size]=\"'medium'\"\n [disabled]=\"isLocked()\"\n (click)=\"toggleDropdown()\"\n (keydown.enter)=\"focusTagDropdown()\"\n cdkOverlayOrigin\n #dropdownTrigger=\"cdkOverlayOrigin\"\n ></ui-button>\n }\n </div>\n\n <div class=\"flex-container\">\n <ui-button\n [variant]=\"'icon-button'\"\n [size]=\"'small'\"\n [ariaLabel]=\"'Scroll left button'\"\n [iconName]=\"'Arrow-chevron-left-in-line'\"\n [disabled]=\"isLocked() || tagsScrollContainer.scrollLeft === 0\"\n (click)=\"tagsScrollContainer.scrollBy({ left: -100, behavior: 'smooth' })\"\n ></ui-button>\n <ui-button\n [variant]=\"'icon-button'\"\n [size]=\"'small'\"\n [iconName]=\"'Arrow-chevron-right-in-line'\"\n [ariaLabel]=\"'Scroll right button'\"\n [disabled]=\"\n isLocked() ||\n tagsScrollContainer.scrollLeft + tagsScrollContainer.clientWidth >= tagsScrollContainer.scrollWidth\n \"\n (click)=\"tagsScrollContainer.scrollBy({ left: 100, behavior: 'smooth' })\"\n ></ui-button>\n </div>\n </div>\n }\n\n @if (showSendButton()) {\n <ui-button\n class=\"send-button\"\n [variant]=\"'secondary'\"\n [iconName]=\"'Arrow-up-in-line'\"\n [tooltip]=\"('COMMON.SEND' | uiTranslate | async)!\"\n [ariaLabel]=\"('COMMON.SEND' | uiTranslate | async)!\"\n [tooltipPosition]=\"'above'\"\n [size]=\"'medium'\"\n [disabled]=\"!canSubmit()\"\n (click)=\"sendPromptData()\"\n ></ui-button>\n }\n </div>\n </div>\n\n @if (errorMessage()) {\n <div class=\"prompt-error\">\n <ui-icon [name]=\"'Error'\" color=\"red\"></ui-icon>\n <span>{{ errorMessage() }}</span>\n </div>\n }\n\n <!-- Dropdown for hidden tags -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"promptOrigin\"\n [cdkConnectedOverlayOpen]=\"isDropdownOpened() && !isLocked()\"\n [cdkConnectedOverlayPositions]=\"dropdownPositions\"\n [cdkConnectedOverlayWidth]=\"promptContainerRef?.offsetWidth || 0\"\n (overlayOutsideClick)=\"dropdownOutsideClick()\"\n >\n <div class=\"dropdown-container\" role=\"list\" #tagDropdown tabindex=\"0\">\n @for (tag of tagsInDropdown(); track tag) {\n <div\n class=\"dropdown-tag\"\n tabindex=\"0\"\n [attr.aria-label]=\"tag.label\"\n role=\"listitem\"\n (click)=\"selectTag(tag, $event)\"\n (keyup.enter)=\"selectTag(tag, $event)\"\n (keyup.space)=\"selectTag(tag, $event)\"\n >\n {{ tag.label }}\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Built-in items dropdown (UI-877) — opens on focus, typing filters by label -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"promptOrigin\"\n [cdkConnectedOverlayOpen]=\"itemsDropdownVisible()\"\n [cdkConnectedOverlayPositions]=\"dropdownPositions\"\n [cdkConnectedOverlayWidth]=\"promptContainerRef?.offsetWidth || 0\"\n (overlayOutsideClick)=\"itemsDropdownOutsideClick($event)\"\n >\n <div class=\"dropdown-container custom-dropdown-container\" (mousedown)=\"$event.preventDefault()\">\n @if (dropdownHeading() || showDropdownClose()) {\n <div class=\"dropdown-header\">\n @if (dropdownHeading()) {\n <span class=\"dropdown-heading\">{{ dropdownHeading() }}</span>\n }\n @if (showDropdownClose()) {\n <ui-button\n class=\"dropdown-close\"\n [variant]=\"'icon-button'\"\n [size]=\"'small'\"\n [iconName]=\"'Close-in-line'\"\n [tooltip]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n [tooltipPosition]=\"'above'\"\n [ariaLabel]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n (click)=\"closeItemsDropdown()\"\n ></ui-button>\n }\n </div>\n }\n <div role=\"list\">\n @for (item of filteredDropdownItems(); track item.id) {\n <div\n class=\"dropdown-tag dropdown-item\"\n tabindex=\"0\"\n role=\"listitem\"\n [attr.aria-label]=\"item.label\"\n (click)=\"selectDropdownItem(item, $event)\"\n (keydown.enter)=\"selectDropdownItem(item, $event)\"\n (keydown.space)=\"selectDropdownItem(item, $event)\"\n >\n @if (item.icon) {\n <ui-icon class=\"dropdown-item-icon\" [name]=\"item.icon\" [color]=\"'inherit'\"></ui-icon>\n }\n <span class=\"dropdown-item-label\">{{ item.label }}</span>\n <ui-icon class=\"dropdown-item-chevron\" [name]=\"'Arrow-chevron-right-in-line'\" [color]=\"'inherit'\"></ui-icon>\n </div>\n }\n </div>\n </div>\n </ng-template>\n</ng-container>\n","queryGuide":{"roles":["input","list","listitem"],"ariaAttributes":["aria-hidden","aria-disabled","aria-busy","aria-label"],"dataTestIds":["prompt-file-input"],"materialComponents":[],"childUIComponents":["ui-skeleton","ui-icon","ui-field","ui-tag","ui-button","ui-badge","ui-overflow-menu"],"suggestedQueries":["screen.getByRole('input')","screen.getByRole('list')","screen.getByRole('listitem')","screen.getByLabelText('label text')","screen.getByTestId('prompt-file-input')"]},"filePath":"components/prompt/prompt.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=19927-107289","lastModified":1786626756410.223},"radial-progress":{"name":"RadialProgressComponent","selector":"ui-radial-progress","category":"radial-progress","description":"A circular progress ring that visualises a single percentage value. Available in multiple sizes (small, medium, large) and weight variants (default and thin). Used in candidate result cards, skill score displays, and assessment completion summaries to show a score at a glance. The large variant supports projected content (typically an Avatar) inside the ring.","importPath":"@testgorilla/tgo-ui/components/radial-progress","moduleName":"RadialProgressComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"RadialProgressSize","defaultValue":"RadialProgressSizeEnum.SMALL","required":false,"alias":null,"transform":null,"description":"The size of the component. Defaults to 'small'.","isSignal":false},{"name":"value","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The current value of the progress. Defaults to 0.","isSignal":false},{"name":"total","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The value of the total progress. Used to set the cap and used for percentage calculations. Defaults to 0.","isSignal":false},{"name":"variant","type":"RadialProgressVariant","defaultValue":"'number'","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"showPercentageSign","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"icon","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Icon for icon button","isSignal":false},{"name":"buttonTooltip","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The tooltip that is displayed on hover. Required for icon-button","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Color of the progress bar.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"showProjectedContent","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"When true (and size === 'large'), the projected content is revealed over\nthe variant's default inner content with a cross-fade animation. Bind to\nthe consumer's externally-controlled state (e.g. a parent card's hover\nor focus-within state). Has no effect when size === 'small' — the slot\nis suppressed via display: none at that size.","isSignal":false}],"outputs":[{"name":"buttonClicked","eventType":"Event","alias":null,"description":"","isSignal":false}],"module":{"name":"RadialProgressComponentModule","imports":["CommonModule","MatProgressSpinnerModule","ButtonComponentModule","RadialProgressComponent"],"exports":["RadialProgressComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"RadialProgressSize","value":"'large' | 'small'"},{"name":"RadialProgressVariant","value":"'number' | 'icon-button' | 'thin' | 'empty' | 'empty-thin'"}],"interfaces":[],"enums":[{"name":"RadialProgressSizeValue","members":[{"name":"LARGE","value":"64"},{"name":"SMALL","value":"40"}]},{"name":"RadialProgressSizeEnum","members":[{"name":"LARGE","value":"'large'"},{"name":"SMALL","value":"'small'"}]}]},"examples":[{"name":"WithProjectedAvatarRevealed","template":"<ui-radial-progress ${argsToTemplate(mergedArgs)}>\n <ui-avatar name=\"Test Gorilla\" size=\"medium\"></ui-avatar>\n </ui-radial-progress>","props":{"size":"RadialProgressSizeEnum.LARGE","value":"25","total":"100","variant":"'thin'","showProjectedContent":"true"}}],"documentation":"A circular progress ring that visualises a single percentage value. Available in multiple sizes (small, medium, large) and weight variants (default and thin). Used in candidate result cards, skill score displays, and assessment completion summaries to show a score at a glance. The large variant supports projected content (typically an Avatar) inside the ring.\n\n## When to use\n\n- **Score is the primary focus** — use the large size to draw the eye immediately\n- **Dense layouts** — use the thin variant where the ring should be present without dominating\n- **Ring needs to trigger an action** — use the icon-button variant when it also has to do something (e.g. expand score detail)\n\n## When NOT to use\n\n- **Non-percentage or unbounded values** → it implies 0–100 by design\n- **More than one large ring per card** → it creates competing focal points\n- **Projected content at small size** → the inner area is too small and the component suppresses projection at that size\n\n## How to use\n\nTo use the Radial Progress component, you need to import the `RadialProgressComponentModule` in your module:\n\n```typescript\nimport { RadialProgressComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n RadialProgressComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-radial-progress [value]=\"25\" [total]=\"100\" size=\"large\" applicationTheme=\"light\"> </ui-radial-progress>\n```\n\nThe component supports several sizes and variants, including an icon-button option that can trigger actions when clicked.\n\n## Projecting alternate inner content\n\nWhen `size=\"large\"`, you can project any content (commonly a `ui-avatar`) into the ring. The projected content stays hidden by default and cross-fades in over the variant's default content when `[showProjectedContent]=\"true\"`.\n\nThe reveal is externally controlled — bind `[showProjectedContent]` to the consumer's own state (typically a parent element's hover or focus-within):\n\n```html\n<ui-radial-progress\n [value]=\"rankingScore\"\n [total]=\"1\"\n variant=\"number\"\n size=\"large\"\n [showProjectedContent]=\"isCardHovered || isCardFocused\"\n>\n <ui-avatar [name]=\"talent.name\" [imageUrl]=\"talent.imageUrl\" size=\"medium\"></ui-avatar>\n</ui-radial-progress>\n```\n\nRecommended pairing: `size=\"medium\"` avatar fits cleanly inside a `size=\"large\"` ring (the inner diameter is ~48px at large size). Any content can be projected — the component renders whatever is given.\n\nAt `size=\"small\"` the projection slot is fully suppressed (`display: none`), regardless of `[showProjectedContent]`. The component renders as a plain small radial-progress.\n","whenToUse":["**Score is the primary focus** — use the large size to draw the eye immediately","**Dense layouts** — use the thin variant where the ring should be present without dominating","**Ring needs to trigger an action** — use the icon-button variant when it also has to do something (e.g. expand score detail)"],"whenNotToUse":["**Non-percentage or unbounded values** → it implies 0–100 by design","**More than one large ring per card** → it creates competing focal points","**Projected content at small size** → the inner area is too small and the component suppresses projection at that size"],"template":"<div\n class=\"radial-progress-container\"\n [attr.theme]=\"applicationTheme\"\n [style.width]=\"progressSize + 'px'\"\n [style.height]=\"progressSize + 'px'\"\n>\n <div class=\"background\" [style.border-width]=\"strokeWidth + 'px'\"></div>\n\n <mat-progress-spinner\n [strokeWidth]=\"strokeWidth\"\n [diameter]=\"progressSize\"\n [value]=\"percentage\"\n ></mat-progress-spinner>\n\n <div\n class=\"inner-container size-{{ size }}\"\n [class.has-revealed-content]=\"showProjectedContent\"\n [class.small-font]=\"(variant === 'thin' || variant === 'empty-thin') && percentage !== 100\"\n [class.tiny-font]=\"percentage === 100 && showPercentageSign && variant === 'thin'\"\n >\n <div class=\"default-content\" [attr.inert]=\"showProjectedContent ? '' : null\">\n @if (variant === 'empty' || variant === 'empty-thin') {\n <div>{{ '-' }}</div>\n } @else if (variant === 'icon-button' && size !== 'small') {\n <ui-button\n [tooltip]=\"buttonTooltip\"\n [iconName]=\"icon\"\n [variant]=\"'icon-button'\"\n (click)=\"onButtonClick($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-button>\n } @else if (size === radialProgressSize.LARGE || variant === 'thin') {\n <div class=\"value\">\n {{ displayValue }}\n </div>\n }\n </div>\n <div class=\"projected-content\" [attr.inert]=\"showProjectedContent ? null : ''\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-progress-spinner"],"childUIComponents":["ui-button"],"suggestedQueries":[]},"filePath":"components/radial-progress/radial-progress.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=488-27282","lastModified":1786626756410.787},"radio-button":{"name":"RadioButtonComponent","selector":"ui-radio-button","category":"forms","description":"A single-choice selector for mutually exclusive options. Users can only select one Radio Button from a group at a time. Used in assessment settings (select scoring method), custom question creation (select answer type), and any form scenario with 2–4 exclusive options that benefit from full visibility.","importPath":"@testgorilla/tgo-ui/components/radio-button","moduleName":"RadioButtonComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the radio button is disabled.\nDefault: false.","isSignal":false},{"name":"allowUnselect","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the radio button can be unchecked.\nDefault: false.","isSignal":false},{"name":"companyColor","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Determines the radio button color.\nDefault: Test Gorilla primary color.","isSignal":false},{"name":"name","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Used to group radios for unique selection.","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Text content will be applied to the input element if present.","isSignal":false},{"name":"multiple","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Determines whether the radio button is a multiple choice cell.\nDefault: false","isSignal":false},{"name":"mediaCard","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Renders the radio button as a media card. The image/video/gif passed via\nthe `[radio-media]` content slot fills the card and the radio + label sit\non top, in the bottom-left, with a dark scrim for contrast.","isSignal":false},{"name":"value","type":"unknown","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Determines value of the radio button.\nDefault: false","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"hasError","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Defines if checkbox has error","isSignal":false},{"name":"ariaLabelledby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"truncateText","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Truncate text if it is too long","isSignal":false},{"name":"alignment","type":"Alignment","defaultValue":"'center'","required":false,"alias":null,"transform":null,"description":"Text alignment","isSignal":false},{"name":"tabIndex","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"Sets the tab index for the checkbox component.\nThis determines the order in which elements receive focus when the user navigates through them using the Tab key.\nDefault value is 0.","isSignal":false},{"name":"hideTooltipOnEllipsis","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Show tooltip when label is truncated","isSignal":false}],"outputs":[{"name":"changeRadio","eventType":"RadioButtonEventType","alias":null,"description":"Event emitted when the checked state of the radio button changes.","isSignal":false}],"module":{"name":"RadioButtonComponentModule","imports":["CommonModule","MatRadioModule","FormsModule","ReactiveFormsModule","MatInputModule","EllipseTextDirective","MatTooltipModule","MatCheckboxModule","FocusVisibleDirective","RadioButtonComponent"],"exports":["RadioButtonComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"RadioButtonEventType","value":"{\n optionName: string;\n optionChecked: boolean;\n}"}],"interfaces":[],"enums":[]},"examples":[{"name":"MultipleChoice","template":"<ui-radio-button [multiple]=\"true\" applicationTheme=\"${appTheme}\" label=\"Single selection - unselected\" [selected]=\"false\" allowUnselect=\"true\" name=\"unchecked\"></ui-radio-button>\n <br>\n <ui-radio-button [multiple]=\"true\" applicationTheme=\"${appTheme}\" label=\"Single selection - selected\" [selected]=\"true\" allowUnselect=\"true\" name=\"checked\"></ui-radio-button>","props":{}},{"name":"WithErrorMultiple","template":"<ui-radio-button [hasError]=\"true\" allowUnselect=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - unselected\"></ui-radio-button>\n <br>\n <ui-radio-button [hasError]=\"true\" allowUnselect=\"true\" applicationTheme=\"${appTheme}\" [multiple]=\"true\" label=\"Multiple selection - unselected\"></ui-radio-button>","props":{}},{"name":"WithErrorSingle","template":"<ui-radio-button [hasError]=\"true\" applicationTheme=\"${appTheme}\" label=\"Single selection - unselected\"></ui-radio-button>\n <br>\n <ui-radio-button [hasError]=\"true\" applicationTheme=\"${appTheme}\" label=\"Single selection - unselected\"></ui-radio-button>","props":{}},{"name":"MediaCard","template":"<div style=\"max-width: 640px;\">\n <div>\n <ui-radio-button\n [mediaCard]=\"true\"\n applicationTheme=\"${appTheme}\"\n name=\"media-card-demo\"\n value=\"card\"\n label=\"Select card\"\n >\n <img radio-media src=\"https://images.unsplash.com/photo-1635776062127-d379bfcba9f8?auto=format&fit=crop&w=600&q=80\" alt=\"Abstract animated sphere\" />\n </ui-radio-button>\n </div>","props":{}}],"documentation":"A single-choice selector for mutually exclusive options. Users can only select one Radio Button from a group at a time. Used in assessment settings (select scoring method), custom question creation (select answer type), and any form scenario with 2–4 exclusive options that benefit from full visibility.\n\n## When to use\n\n- **Shared group label** — always group Radio Buttons under a label that describes the choice being made\n- **Pre-selected default** — pre-select the most common or default option; never leave a radio group unselected in an existing form\n- **2–4 visible options** — use Radio Buttons when seeing all options simultaneously helps the user choose\n\n## When NOT to use\n\n- **More than 4–5 options** → switch to a Dropdown\n- **A single Radio Button on its own** → it's meaningless without a group of at least 2\n- **Multi-select** → use Checkboxes instead\n\n## How to use\n\nTo use the Radio Button component, you need to import the `RadioButtonComponentModule` in your module:\n\n```typescript\nimport { RadioButtonComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n RadioButtonComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-radio-button\n label=\"Option 1\"\n [selected]=\"isSelected\"\n [disabled]=\"isDisabled\"\n name=\"options\"\n applicationTheme=\"light\"\n (selectedChange)=\"onSelectionChange($event)\"\n>\n</ui-radio-button>\n```\n\nFor multiple radio buttons in a group:\n\n```html\n<ui-radio-button name=\"colorGroup\" [value]=\"'red'\" [groupValue]=\"selectedColor\" label=\"Red\"></ui-radio-button>\n<ui-radio-button name=\"colorGroup\" [value]=\"'blue'\" [groupValue]=\"selectedColor\" label=\"Blue\"></ui-radio-button>\n<ui-radio-button name=\"colorGroup\" [value]=\"'green'\" [groupValue]=\"selectedColor\" label=\"Green\"></ui-radio-button>\n```\n\nThe radio button component can be used for single selection among a set of options.\n","whenToUse":["**Shared group label** — always group Radio Buttons under a label that describes the choice being made","**Pre-selected default** — pre-select the most common or default option; never leave a radio group unselected in an existing form","**2–4 visible options** — use Radio Buttons when seeing all options simultaneously helps the user choose"],"whenNotToUse":["**More than 4–5 options** → switch to a Dropdown","**A single Radio Button on its own** → it's meaningless without a group of at least 2","**Multi-select** → use Checkboxes instead"],"template":"<div\n #radioContainer\n tabindex=\"0\"\n [class]=\"multiple ? 'radio-container' : 'radio'\"\n [class.multiple-disabled]=\"disabled && multiple\"\n [class.media-card]=\"mediaCard\"\n [class.media-card-selected]=\"mediaCard && selected\"\n [ngClass]=\"classMultiple\"\n [attr.alignment]=\"alignment\"\n [attr.truncate-text]=\"truncateText\"\n [attr.theme]=\"applicationTheme\"\n [attr.hasError]=\"hasError\"\n (keydown.enter)=\"clickRadio(); radioContainer.focus()\"\n (keydown.space)=\"clickRadio(); radioContainer.focus()\"\n>\n @if (mediaCard) {\n <div class=\"media-card-wrapper\" aria-hidden=\"true\">\n <ng-content select=\"[radio-media]\"></ng-content>\n </div>\n }\n <mat-radio-button\n #radio\n [checked]=\"selected\"\n [disabled]=\"disabled\"\n [name]=\"name\"\n [value]=\"value\"\n class=\"radio-button\"\n [required]=\"ariaRequired\"\n [aria-label]=\"ariaLabel\"\n [aria-labelledby]=\"ariaLabelledby ? ariaLabelledby : label\"\n [aria-describedby]=\"ariaDescribedby\"\n (keydown.enter)=\"$event.preventDefault()\"\n (keydown.space)=\"$event.preventDefault()\"\n [tabIndex]=\"tabIndex\"\n >\n @if (label) {\n <span\n #tooltip=\"matTooltip\"\n class=\"label\"\n uiEllipseText\n (onChangeTextState)=\"onLabelEllipsisChange($event)\"\n [matTooltip]=\"showTooltip ? label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >{{ label }}</span\n >\n }\n <ng-content select=\"[radio-label]\"></ng-content>\n </mat-radio-button>\n</div>\n","queryGuide":{"roles":["radio"],"ariaAttributes":["aria-hidden"],"dataTestIds":[],"materialComponents":["mat-radio-button"],"childUIComponents":[],"suggestedQueries":["screen.getByRole('radio')"]},"filePath":"components/radio-button/radio-button.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=3132-19050","lastModified":1786626756411.3616},"rating":{"name":"RatingComponent","selector":"ui-rating","category":"rating","description":"An interactive scoring control that lets hiring managers rate candidates on a scale. Supports emoji reactions, star ratings, and custom colour ranges. Used on candidate result pages and assessment review screens to capture qualitative assessments alongside objective test scores.","importPath":"@testgorilla/tgo-ui/components/rating","moduleName":"RatingComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme where accordion is used","isSignal":false},{"name":"value","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The value representing the rating.","isSignal":false},{"name":"theme","type":"RatingComponentTheme","defaultValue":"'block'","required":false,"alias":null,"transform":null,"description":"The theme of the rating component. Defaults \"block\".","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A flag indicating whether the rating component is disabled.","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"barWidth","type":"number","defaultValue":"35","required":false,"alias":null,"transform":null,"description":"The width of the rating bar.","isSignal":false},{"name":"canUnselect","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"A flag indicating whether the rating can be unselected by clicking on the same value.\nWhen false, clicking on the same value will not change the rating.","isSignal":false},{"name":"length","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[{"name":"valueChange","eventType":"number","alias":null,"description":"","isSignal":false}],"module":{"name":"RatingComponentModule","imports":["CommonModule","MatInputModule","RatingComponent","HalfStarPipe"],"exports":["RatingComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[{"name":"Default","template":"<ui-rating ${argsToTemplate(args)} applicationTheme=\"${appTheme}\"></ui-rating>","props":{}},{"name":"CustomColor","template":"<div style=\"--ui-rating-color: #2563eb;\">\n <ui-rating ${argsToTemplate(args)} applicationTheme=\"${appTheme}\"></ui-rating>\n </div>","props":{"theme":"'stars'","value":"3.5"}}],"documentation":"An interactive scoring control that lets hiring managers rate candidates on a scale. Supports emoji reactions, star ratings, and custom colour ranges. Used on candidate result pages and assessment review screens to capture qualitative assessments alongside objective test scores.\n\n## When to use\n\n- **Stars variant** for familiar, immediately understood rating patterns\n- **Emoji variants** when the scale maps to distinct sentiment states — they're more expressive for qualitative feedback\n- **Labelled scale extremes** so users understand what low and high mean in context\n\n## When NOT to use\n\n- **Objective pass/fail criteria** → use a Badge or status indicator instead\n- **Required fields** → don't allow the rating to be submitted in an unset state; show a clear validation error\n- **Cannot Unselect mode** → don't use it without clear signalling, as users expect to be able to clear a rating\n\n## How to use\n\nTo use the Rating component, you need to import the `RatingComponentModule` in your module:\n\n```typescript\nimport { RatingComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n RatingComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-rating [value]=\"3\" theme=\"stars\" [length]=\"5\"></ui-rating>\n```\n","whenToUse":["**Stars variant** for familiar, immediately understood rating patterns","**Emoji variants** when the scale maps to distinct sentiment states — they're more expressive for qualitative feedback","**Labelled scale extremes** so users understand what low and high mean in context"],"whenNotToUse":["**Objective pass/fail criteria** → use a Badge or status indicator instead","**Required fields** → don't allow the rating to be submitted in an unset state; show a clear validation error","**Cannot Unselect mode** → don't use it without clear signalling, as users expect to be able to clear a rating"],"template":"<div class=\"rating\" (mouseleave)=\"onHover(-1)\" [attr.aria-label]=\"ariaLabel\" [attr.aria-required]=\"ariaRequired\">\n @if (theme === 'block') {\n @for (item of items; track item) {\n <div\n class=\"rating-item\"\n [attr.theme]=\"applicationTheme\"\n tabindex=\"0\"\n (click)=\"select(item)\"\n (keydown.enter)=\"select(item)\"\n (mouseenter)=\"onHover(item)\"\n [ngClass]=\"{ 'rating-item-selected': item < value, 'rating-item-hover': item < hoverIndex }\"\n [ngStyle]=\"{ 'width.px': barWidth }\"\n ></div>\n }\n } @else {\n <div class=\"star-container\" [attr.theme]=\"applicationTheme\">\n @for (item of items; track item; let i = $index) {\n <div\n #star\n class=\"star\"\n tabindex=\"0\"\n [tabIndex]=\"0\"\n (mouseenter)=\"onHover(item)\"\n (mouseleave)=\"onHover(-1)\"\n [ngClass]=\"{\n filled: value > i,\n hover: item < hoverIndex && (value ? item >= value : true),\n disabled,\n 'filled-half': i + 1 | halfStar: value,\n }\"\n (click)=\"select(item); star.blur()\"\n (keydown.enter)=\"select(item); star.blur()\"\n ></div>\n }\n </div>\n }\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label","aria-required"],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":["screen.getByLabelText('label text')"]},"filePath":"components/rating/rating.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=848-12810","lastModified":1786626756411.8206},"scale":{"name":"ScaleComponent","selector":"ui-scale","category":"scale","description":"A horizontal row of numbered, selectable points used for Likert-style or custom-range rating questions. Used in TestGorilla's personality, culture-fit, and custom question assessments where candidates rate agreement, frequency, or preference on a numeric scale.","importPath":"@testgorilla/tgo-ui/components/scale","moduleName":"ScaleComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"selectedIndex","type":"number | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"The value representing the selected item of the scale.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"companyColor","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Color of the option hover.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"length","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The max number of items in the scale.","isSignal":false}],"outputs":[{"name":"selectedIndexChange","eventType":"number","alias":null,"description":"","isSignal":false}],"module":{"name":"ScaleComponentModule","imports":["CommonModule","ScaleComponent"],"exports":["ScaleComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A horizontal row of numbered, selectable points used for Likert-style or custom-range rating questions. Used in TestGorilla's personality, culture-fit, and custom question assessments where candidates rate agreement, frequency, or preference on a numeric scale.\n\n## When to use\n\n- **Numeric scale responses** — when the assessment question demands one (Likert, satisfaction, agreement, etc.)\n- **Anchored endpoints** — label both ends of the scale to anchor meaning (e.g. 1 = Strongly disagree, 5 = Strongly agree)\n- **Scales above 5 points** — use the Large Range variant\n\n## When NOT to use\n\n- **Binary yes/no questions** → use a Radio Button pair instead\n- **More than a 10-point scale** → above 10 points, discrimination between adjacent values becomes meaningless\n- **Without visible point labels** → unlabelled scales are inaccessible\n\n## How to use\n\nTo use the Scale component, you need to import the `ScaleComponentModule` in your module:\n\n```typescript\nimport { ScaleComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ScaleComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-scale [length]=\"5\" [selectedIndex]=\"2\" applicationTheme=\"light\" (select)=\"onScaleSelect($event)\"> </ui-scale>\n```\n\nThe component creates a visual scale with selectable points, useful for rating and evaluation interfaces.\n","whenToUse":["**Numeric scale responses** — when the assessment question demands one (Likert, satisfaction, agreement, etc.)","**Anchored endpoints** — label both ends of the scale to anchor meaning (e.g. 1 = Strongly disagree, 5 = Strongly agree)","**Scales above 5 points** — use the Large Range variant"],"whenNotToUse":["**Binary yes/no questions** → use a Radio Button pair instead","**More than a 10-point scale** → above 10 points, discrimination between adjacent values becomes meaningless","**Without visible point labels** → unlabelled scales are inaccessible"],"template":"<div\n [style.--scale-count]=\"items.length\"\n class=\"scale\"\n [ngStyle]=\"styles()\"\n [ngClass]=\"{ 'scale-classic': applicationTheme === 'classic' }\"\n>\n @for (item of items; track item) {\n <div\n class=\"scale-item\"\n (click)=\"select(item)\"\n #scaleItem\n role=\"radio\"\n (keydown)=\"onKeyDown($event, item)\"\n (focus)=\"onFocus(item)\"\n [class.scale-item-focus-prev]=\"selectedIndex && selectedIndex > 0 ? item === selectedIndex - 1 : false\"\n [attr.tabindex]=\"selectedIndex === null ? (item === 0 ? '0' : '-1') : item === selectedIndex ? '0' : '-1'\"\n [attr.aria-checked]=\"\n selectedIndex === null ? (item === 0 ? 'true' : 'false') : item === selectedIndex ? 'true' : 'false'\n \"\n [attr.aria-label]=\"getAriaLabel(item)\"\n [ngClass]=\"{ 'scale-item-selected': item === selectedIndex }\"\n >\n {{ item + 1 }}\n </div>\n }\n</div>\n","queryGuide":{"roles":["radio"],"ariaAttributes":["aria-checked","aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":["screen.getByRole('radio')","screen.getByLabelText('label text')"]},"filePath":"components/scale/scale.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=822-17426","lastModified":1786626756412.4385},"scale-table":{"name":"ScaleTableComponent","selector":"ui-scale-table","category":"scale-table","description":"A tabular layout of multiple Scale rows, each representing a separate question with a shared label axis. Used in personality and multi-item psychometric assessments where candidates respond to a battery of questions using the same rating scale. The shared axis reduces repetition and keeps cognitive load low.","importPath":"@testgorilla/tgo-ui/components/scale-table","moduleName":"ScaleTableComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":true},{"name":"labels","type":"string[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Defines description of scales","isSignal":true},{"name":"scaleItems","type":"ScaleItem[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Defines items with their text and it's values","isSignal":true},{"name":"hideBetterExperience","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"scaleItemChange","eventType":"{\n item: ScaleItem;\n label: string;\n }","alias":null,"description":"","isSignal":true},{"name":"scaleItemsChange","eventType":"ScaleItem[]","alias":null,"description":"Two-way binding change event for scaleItems","isSignal":true},{"name":"hideBetterExperienceChange","eventType":"boolean","alias":null,"description":"Two-way binding change event for hideBetterExperience","isSignal":true}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"ScaleItem","properties":[{"name":"length","type":"number","optional":true},{"name":"value","type":"number","optional":true},{"name":"text","type":"string","optional":false}]}],"enums":[]},"examples":[],"documentation":"A tabular layout of multiple Scale rows, each representing a separate question with a shared label axis. Used in personality and multi-item psychometric assessments where candidates respond to a battery of questions using the same rating scale. The shared axis reduces repetition and keeps cognitive load low.\n\n## When to use\n\n- **3 or more items** that share the same response scale\n- **Concise row labels** — keep them short enough to fit on a single line at the default column width\n- **A full set of axis labels** — define every axis label, as they apply to every row and must be meaningful for all items\n\n## When NOT to use\n\n- **Mixed scale ranges** → don't combine items with different scales; all rows must use the same scale\n- **A single question** → use the Scale component on its own instead\n- **Truncated item labels** → don't cut off labels without a tooltip or expand behaviour\n\n## How to use\n\nTo use the Scale Table component, you need to import the `ScaleTableComponent` in your module or component:\n\n```typescript\nimport { ScaleTableComponent } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ScaleTableComponent,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-scale-table\n [scaleItems]=\"[\n { text: 'Development Stage 1' },\n { text: 'Development Stage 2', value: 1} // with pre selected value\n ]\"\n/>\n```\n\nThis component displays a table of scales with labels, useful for multi-criteria evaluations.\n","whenToUse":["**3 or more items** that share the same response scale","**Concise row labels** — keep them short enough to fit on a single line at the default column width","**A full set of axis labels** — define every axis label, as they apply to every row and must be meaningful for all items"],"whenNotToUse":["**Mixed scale ranges** → don't combine items with different scales; all rows must use the same scale","**A single question** → use the Scale component on its own instead","**Truncated item labels** → don't cut off labels without a tooltip or expand behaviour"],"template":"<div class=\"scale-table\">\n <div class=\"better-experience\" [class.hide]=\"hideBetterExperience()\">\n <ui-icon [size]=\"'24'\" [name]=\"'Device-rotate-in-line'\" class=\"mobile-landscape-icon\" />\n <p>{{ 'SCALE_TABLE.ROTATE_DEVICE' | uiTranslate | async }}</p>\n <ui-button\n iconName=\"Close\"\n variant=\"icon-button\"\n size=\"small\"\n [justIcon]=\"true\"\n [tooltip]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n (click)=\"hideBetterExperience.set(true)\"\n />\n </div>\n <!-- Header -->\n <div tabindex=\"0\" [attr.aria-label]=\"'scale header'\" class=\"scale-table-header\">\n @for (label of labels(); track $index) {\n <div\n tabindex=\"0\"\n role=\"heading\"\n [attr.aria-level]=\"3\"\n [attr.aria-label]=\"label + ' as ' + ($index + 1)\"\n class=\"scale-size\"\n >\n {{ label }}\n </div>\n }\n </div>\n <!-- Scale Items -->\n @for (scale of scaleItems(); track scale.text) {\n <div class=\"scale-table-row\" tabindex=\"0\" role=\"radiogroup\" [attr.aria-label]=\"scale.text\">\n <div class=\"scale-text\">{{ scale.text }}</div>\n <ui-scale\n [selectedIndex]=\"scale.value ?? null\"\n [length]=\"scale.length ?? labels().length\"\n [applicationTheme]=\"applicationTheme()\"\n (selectedIndexChange)=\"select($index, $event)\"\n />\n </div>\n }\n</div>\n","queryGuide":{"roles":["heading","radiogroup"],"ariaAttributes":["aria-label","aria-level"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon","ui-button","ui-scale"],"suggestedQueries":["screen.getByRole('heading')","screen.getByRole('radiogroup')","screen.getByLabelText('label text')"]},"filePath":"components/scale-table/scale-table.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=822-17426","lastModified":1786626756412.046},"segmented-bar":{"name":"SegmentedBarComponent","selector":"ui-segmented-bar","category":"segmented-bar","description":"A horizontal bar divided into proportional coloured segments, each representing a slice of a whole. Used in assessment results to visualise score distributions across categories, and in candidate pipeline views to show stage-by-stage counts.","importPath":"@testgorilla/tgo-ui/components/segmented-bar","moduleName":"SegmentedBarComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"width","type":"unknown","defaultValue":"'100%'","required":false,"alias":null,"transform":null,"description":"The width of the segmented bar. Defaults to '100%'.\nYou can use '100%' or '100px' for this field. 100 is just an example","isSignal":false},{"name":"size","type":"SegmentedBarVariant","defaultValue":"'large'","required":false,"alias":null,"transform":null,"description":"The size variant of the segmented bar. Can be 'small' or 'large'.\nDefaults to 'large'.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"tooltipStats","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Groups the tooltips into one template","isSignal":false},{"name":"segments","type":"SegmentedBar[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The segments to be displayed in the segmented bar.","isSignal":false}],"outputs":[],"module":{"name":"SegmentedBarComponentModule","imports":["CommonModule","MatTooltipModule","MatInputModule","IconComponentModule","TooltipComponentModule","SegmentedBarComponent"],"exports":["SegmentedBarComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SegmentedBarVariant","value":"'large' | 'small'"},{"name":"SegmentedBarColor","value":"'teal' | 'rhubarb' | 'light-teal' | 'grey'"},{"name":"SegmentedBarWithPercentage","value":"SegmentedBar & { percentage: string }"}],"interfaces":[{"name":"SegmentedBar","properties":[{"name":"value","type":"number","optional":false},{"name":"color","type":"SegmentedBarColor","optional":false},{"name":"tooltip","type":"string","optional":true},{"name":"icon","type":"IconName","optional":true}]}],"enums":[]},"examples":[],"documentation":"A horizontal bar divided into proportional coloured segments, each representing a slice of a whole. Used in assessment results to visualise score distributions across categories, and in candidate pipeline views to show stage-by-stage counts.\n\n## When to use\n\n- **Segments that sum to a meaningful whole** — the bar implies a 100% total\n- **Tooltips on segments** when exact values matter and labels can't fit in the bar\n- **The small variant in data table cells** — and the default size for standalone cards\n\n## When NOT to use\n\n- **More than 5–6 segments** → beyond that, the proportional differences become impossible to read\n- **Sequential or time-based data** → use a ProgressBar or timeline instead\n- **Single-value data** → a single-segment bar is just a ProgressBar without the semantics\n\n## How to use\n\nTo use the Segmented Bar component, you need to import the `SegmentedBarComponentModule` in your module:\n\n```typescript\nimport { SegmentedBarComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SegmentedBarComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-segmented-bar\n [segments]=\"[\n { value: 3, color: 'teal', tooltip: 'Completed' },\n { value: 1, color: 'grey', tooltip: 'Pending' },\n { value: 2, color: 'rhubarb', tooltip: 'Failed' }\n ]\"\n applicationTheme=\"light\"\n>\n</ui-segmented-bar>\n```\n\nThis component displays a horizontal bar divided into colored segments, useful for visualizing proportional data.\n","whenToUse":["**Segments that sum to a meaningful whole** — the bar implies a 100% total","**Tooltips on segments** when exact values matter and labels can't fit in the bar","**The small variant in data table cells** — and the default size for standalone cards"],"whenNotToUse":["**More than 5–6 segments** → beyond that, the proportional differences become impossible to read","**Sequential or time-based data** → use a ProgressBar or timeline instead","**Single-value data** → a single-segment bar is just a ProgressBar without the semantics"],"template":"<div\n class=\"segmented-bar-container\"\n [attr.theme]=\"applicationTheme\"\n [ngClass]=\"'segment-size-' + size\"\n [style.width]=\"width\"\n [uiTooltipTemplate]=\"segmentedStats\"\n [showTooltip]=\"tooltipStats\"\n [attr.aria-label]=\"accessibleValueText\"\n role=\"graphics-object\"\n [tabIndex]=\"0\"\n>\n @for (segment of segmentList; track segment) {\n @if (segment.value) {\n <div\n class=\"segmented-bar\"\n [ngClass]=\"segment.color\"\n [ngStyle]=\"{ flex: segment.value }\"\n [matTooltip]=\"!tooltipStats ? (segment.tooltip ?? '') : ''\"\n [matTooltipClass]=\"applicationTheme\"\n [attr.aria-hidden]=\"true\"\n [attr.aria-required]=\"ariaRequired\"\n >\n @if (segment.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"segment.icon\"></ui-icon>\n }\n <span>{{ segment.value }}</span>\n </div>\n }\n }\n</div>\n<ng-template #segmentedStats>\n <div class=\"stats-container\">\n <div class=\"name-column\">\n @for (segment of segmentList; track segment.value) {\n <div class=\"segment\">\n <div [class]=\"'point ' + segment.color\"></div>\n <span [class]=\"'label ' + segment.color\">{{ segment.tooltip }}</span>\n </div>\n }\n </div>\n\n <div class=\"value-column\">\n @for (segment of segmentList; track segment.value) {\n <div class=\"segment\">\n <span>{{ segment.value }}</span>\n </div>\n }\n </div>\n\n <div class=\"percentage-column\">\n @for (segment of segmentList; track segment.value) {\n <div class=\"segment\">\n <span>{{ segment.percentage }}</span>\n </div>\n }\n </div>\n </div>\n</ng-template>\n","queryGuide":{"roles":["graphics-object"],"ariaAttributes":["aria-label","aria-hidden","aria-required"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('graphics-object')","screen.getByLabelText('label text')"]},"filePath":"components/segmented-bar/segmented-bar.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2032-25771","lastModified":1786626756412.8223},"segmented-button":{"name":"SegmentedButtonComponent","selector":"ui-segmented-button","category":"segmented-button","description":"A group of mutually exclusive toggle buttons that switch between views or modes within a page section. Acts like a radio button group but with button-level visual weight. Used for switching between 'Grid' and 'List' views, toggling between 'All candidates' and 'Shortlisted', or selecting report tabs.","importPath":"@testgorilla/tgo-ui/components/segmented-button","moduleName":"SegmentedButtonComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"buttonConfig","type":"SegmentedButton[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Input property to set the buttons for the segmented button.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the component is disabled.\nDefault: false.","isSignal":false},{"name":"fullWidth","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Indicator of the Segmented Button width","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"value","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Selected button","isSignal":false}],"outputs":[{"name":"buttonSelected","eventType":"string","alias":null,"description":"","isSignal":false}],"module":{"name":"SegmentedButtonComponentModule","imports":["CommonModule","MatButtonToggleModule","FormsModule","MatTooltipModule","SegmentedButtonComponent"],"exports":["SegmentedButtonComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"SegmentedButton","properties":[{"name":"value","type":"string","optional":false},{"name":"label","type":"string","optional":true},{"name":"template","type":"TemplateRef<unknown>","optional":true},{"name":"disabled","type":"boolean","optional":true},{"name":"tooltip","type":"string","optional":true}]}],"enums":[]},"examples":[{"name":"Default","template":"<ui-segmented-button [buttonConfig]=\"buttonConfig\" applicationTheme=\"${appTheme}\"></ui-segmented-button>","props":{"buttonConfig":"[","label":"'StartButton', value: '1'"}},{"name":"Disabled","template":"<ui-segmented-button [buttonConfig]=\"buttonConfig\" applicationTheme=\"${appTheme}\"></ui-segmented-button>","props":{"buttonConfig":"[","label":"'StartButton', value: '1', disabled: true"}},{"name":"WithTemplate","template":"<ng-template #badgeTpl>\n <ui-badge\n label=\"Suggested\"\n variant=\"primary\"\n rebrandColor=\"info\"\n style=\"position: absolute; top: -10px; left: -20px\"\n ></ui-badge>\n Label\n </ng-template>\n <ui-segmented-button applicationTheme=\"${appTheme}\"\n [buttonConfig]=\"\n [\n { value: 1, template: badgeTpl },\n { label: 'MiddleButton', value: 2 },\n { label: 'EndButton', value: 3 },\n ]\"\n >\n </ui-segmented-button>","props":{}}],"documentation":"A group of mutually exclusive toggle buttons that switch between views or modes within a page section. Acts like a radio button group but with button-level visual weight. Used for switching between 'Grid' and 'List' views, toggling between 'All candidates' and 'Shortlisted', or selecting report tabs.\n\n## When to use\n\n- **2–4 mutually exclusive options** that change the current view or filter state — not for navigation\n- **Pre-select a default** — never render the group with nothing selected, so the most common option is chosen up front\n- **The Template variant** when button labels need icons or complex formatting\n\n## When NOT to use\n\n- **Primary page navigation** → use Tabs instead\n- **More than 4 segments** → beyond that the component becomes too wide to scan\n- **Non-exclusive selections** → if users can pick more than one, use checkboxes or a multi-select filter\n\n## How to use\n\nTo use the Segmented Button component, you need to import the `SegmentedButtonComponentModule` in your module:\n\n```typescript\nimport { SegmentedButtonComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SegmentedButtonComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-segmented-button\n [buttonConfig]=\"[\n { label: 'Day', value: 'day' },\n { label: 'Week', value: 'week' },\n { label: 'Month', value: 'month' }\n ]\"\n applicationTheme=\"light\"\n (valueChange)=\"onSelectionChange($event)\"\n>\n</ui-segmented-button>\n```\n\nThe component can also accept custom templates for more complex button content.\n","whenToUse":["**2–4 mutually exclusive options** that change the current view or filter state — not for navigation","**Pre-select a default** — never render the group with nothing selected, so the most common option is chosen up front","**The Template variant** when button labels need icons or complex formatting"],"whenNotToUse":["**Primary page navigation** → use Tabs instead","**More than 4 segments** → beyond that the component becomes too wide to scan","**Non-exclusive selections** → if users can pick more than one, use checkboxes or a multi-select filter"],"template":"<mat-button-toggle-group\n class=\"segmented-button-container\"\n [attr.theme]=\"applicationTheme\"\n [ngClass]=\"{ disabled, 'full-width': fullWidth }\"\n [value]=\"value\"\n [hideSingleSelectionIndicator]=\"true\"\n (change)=\"manualChangeValue($event)\"\n>\n @for (button of buttonConfig; track button) {\n <mat-button-toggle\n [disableRipple]=\"true\"\n [value]=\"button.value\"\n [ngClass]=\"{ disabled: button.disabled }\"\n [matTooltip]=\"button.tooltip ?? ''\"\n >\n @if (button.template) {\n <ng-container [ngTemplateOutlet]=\"button.template\"></ng-container>\n } @else {\n {{ button.label }}\n }\n </mat-button-toggle>\n }\n</mat-button-toggle-group>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-button-toggle-group","mat-button-toggle"],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/segmented-button/segmented-button.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=822-17426","lastModified":1786626756413.244},"segmented-toggle":{"name":"SegmentedToggleComponent","selector":"ui-segmented-toggle","category":"segmented-toggle","description":"SegmentedToggleComponent component","importPath":"@testgorilla/tgo-ui/components/segmented-toggle","moduleName":"SegmentedToggleComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"options","type":"SegmentedToggleOption<T>[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"value","type":"T","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"valueChange","eventType":"T","alias":null,"description":"Two-way binding change event for value","isSignal":true}],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"SegmentedToggleOption","properties":[{"name":"value","type":"T","optional":false},{"name":"label","type":"string","optional":true},{"name":"icon","type":"IconName","optional":true},{"name":"iconAriaLabel","type":"string","optional":true},{"name":"brand","type":"boolean","optional":true}]}],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<div class=\"ui-segmented-toggle__group\" role=\"group\" [attr.aria-label]=\"ariaLabel()\">\n <span\n class=\"ui-segmented-toggle__indicator\"\n [class.ui-segmented-toggle__indicator--hidden]=\"selectedIndex() < 0\"\n [class.ui-segmented-toggle__indicator--animated]=\"animated()\"\n aria-hidden=\"true\"\n [style.transform]=\"'translateX(' + indicatorOffset() + 'px) translateY(' + indicatorTop() + 'px)'\"\n [style.width.px]=\"indicatorWidth()\"\n [style.height.px]=\"indicatorHeight()\"\n ></span>\n\n @for (option of options(); track option.value) {\n <button\n #optionButton\n type=\"button\"\n class=\"ui-segmented-toggle__option\"\n [attr.aria-pressed]=\"isSelected(option.value)\"\n [attr.aria-label]=\"fallbackAriaLabel(option)\"\n [disabled]=\"disabled()\"\n (click)=\"select(option.value)\"\n >\n @if (option.icon) {\n <ui-icon\n class=\"ui-segmented-toggle__icon\"\n [name]=\"option.icon\"\n size=\"16\"\n [attr.aria-hidden]=\"option.iconAriaLabel ? null : 'true'\"\n ></ui-icon>\n @if (option.iconAriaLabel) {\n <span class=\"ui-segmented-toggle__sr-only\">{{ option.iconAriaLabel }}</span>\n }\n }\n @if (option.label) {\n <span class=\"ui-segmented-toggle__label\">{{ option.label }}</span>\n }\n </button>\n }\n</div>\n","queryGuide":{"roles":["group"],"ariaAttributes":["aria-hidden","aria-label","aria-pressed"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('group')","screen.getByLabelText('label text')"]},"filePath":"components/segmented-toggle/segmented-toggle.component.ts","supportsTheme":true,"lastModified":1786626756413.6987},"selectable-card":{"name":"SelectableCardComponent","selector":"ui-selectable-card","category":"selectable-card","description":"A card with an integrated checkbox that lets users select it as part of a multi-selection group. Used in assessment test selection, bulk candidate actions setup, and configuration screens where users need to pick multiple items from a presented set.","importPath":"@testgorilla/tgo-ui/components/selectable-card","moduleName":"SelectableCardComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"CardSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"Card size. Defaults to \"medium\".","isSignal":true},{"name":"selectedVariant","type":"CardVariant","defaultValue":"'state'","required":false,"alias":null,"transform":null,"description":"Card variant/design for selected state. Defaults to \"default\".","isSignal":true},{"name":"selected","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the card is selected (input from parent).","isSignal":true},{"name":"isCheckboxDisabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the checkbox is disabled.","isSignal":true},{"name":"hasError","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the card is in an error state, which renders the checkbox in red.","isSignal":true}],"outputs":[{"name":"cardSelected","eventType":"boolean","alias":null,"description":"Event emitted when the card is selected, with the current selected state.","isSignal":true}],"module":{"name":"SelectableCardComponentModule","imports":["CommonModule","CardComponentModule","CheckboxComponentModule","SelectableCardComponent"],"exports":["SelectableCardComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"A card with an integrated checkbox that lets users select it as part of a multi-selection group. Used in assessment test selection, bulk candidate actions setup, and configuration screens where users need to pick multiple items from a presented set.\n\n## When to use\n\n- **The card itself is the selectable unit** — the checkbox and card act as a single interactive target\n- **The Error variant** — use it to surface validation errors (e.g. 'You must select at least one test') on individual cards\n- **Grouped in a grid or list** — arrange SelectableCards so selection state is immediately visible across all items\n\n## When NOT to use\n\n- **Single-select flows** → if users can only choose one, use a Radio Button or the State Card variant\n- **Disabled cards without explanation** → always explain why via a tooltip or adjacent message\n- **Card content as the primary action** → don't use it when the card needs to navigate (e.g. to a detail page); selection and navigation shouldn't compete\n\n## How to use\n\nTo use the SelectableCard component, you need to import the `SelectableCardComponentModule` in your module:\n\n```typescript\nimport { SelectableCardComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SelectableCardComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-selectable-card\n [size]=\"'medium'\"\n [selected]=\"isSelected\"\n [isCheckboxDisabled]=\"isDisabled\"\n (cardSelected)=\"onCardSelected($event)\"\n>\n <ui-selectable-card-label>Card Label</ui-selectable-card-label>\n <ui-selectable-card-content>\n <div>Your content goes here</div>\n </ui-selectable-card-content>\n</ui-selectable-card>\n```\n\n## Inputs\n\n- `size`: Card size (`'small' | 'medium' | 'large'`). Defaults to `'medium'`.\n- `selected`: Whether the card is selected from parent (boolean). Defaults to `false`. This updates the internal selected state.\n- `isCheckboxDisabled`: Whether the card selection is disabled (boolean). Defaults to `false`.\n- `hasError`: Whether the card is in an error state, which renders the checkbox in red (boolean). Defaults to `false`.\n\n## Outputs\n\n- `cardSelected`: Emitted when the card is selected, with the current selected state (boolean).\n\n## Content Projection\n\n- `ui-selectable-card-label`: Content projected to the checkbox label area.\n- `ui-selectable-card-content`: Content projected to the main card content area.\n","whenToUse":["**The card itself is the selectable unit** — the checkbox and card act as a single interactive target","**The Error variant** — use it to surface validation errors (e.g. 'You must select at least one test') on individual cards","**Grouped in a grid or list** — arrange SelectableCards so selection state is immediately visible across all items"],"whenNotToUse":["**Single-select flows** → if users can only choose one, use a Radio Button or the State Card variant","**Disabled cards without explanation** → always explain why via a tooltip or adjacent message","**Card content as the primary action** → don't use it when the card needs to navigate (e.g. to a detail page); selection and navigation shouldn't compete"],"template":"<ui-card\n class=\"selectable-card\"\n [size]=\"size()\"\n [selected]=\"isSelected()\"\n [allowSelect]=\"!isCheckboxDisabled()\"\n [allowFocus]=\"false\"\n [variant]=\"isCheckboxDisabled() ? 'default' : isSelected() && selectedVariant() ? selectedVariant() : 'state'\"\n (cardSelected)=\"handleSelect()\"\n>\n <ui-checkbox\n class=\"selectable-card-checkbox\"\n [checked]=\"isSelected()\"\n [disabled]=\"isCheckboxDisabled()\"\n [hasError]=\"hasError()\"\n >\n <div checkbox-label>\n <ng-content select=\"[ui-selectable-card-label]\"></ng-content>\n </div>\n </ui-checkbox>\n <ng-content select=\"[ui-selectable-card-content]\"></ng-content>\n</ui-card>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-card","ui-checkbox"],"suggestedQueries":[]},"filePath":"components/selectable-card/selectable-card.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=369-19097","lastModified":1786626756414.4556},"side-navigation":{"name":"SideNavigationComponent","selector":"ui-side-navigation","category":"side-navigation","description":"SideNavigationComponent component","importPath":"@testgorilla/tgo-ui/components/side-navigation","moduleName":"SideNavigationComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"activeRoute","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"mainCta","type":"NavigationCta","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"mainNavigation","type":"NavigationItem[]","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"secondaryNavigation","type":"NavigationItem[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"contentTemplateExtendedRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"contentTemplateCollapsedRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"account","type":"NavigationAccount","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"appContentTemplateRef","type":"TemplateRef<unknown>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"collapseToggleLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Optional override; falls back to `SIDE_NAVIGATION.COLLAPSE_LABEL` translation.","isSignal":true},{"name":"expandToggleLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Optional override; falls back to `SIDE_NAVIGATION.EXPAND_LABEL` translation.","isSignal":true},{"name":"mobileMenuLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Optional override; falls back to `SIDE_NAVIGATION.MOBILE_OPEN_LABEL` translation.","isSignal":true},{"name":"mobileCloseLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Optional override; falls back to `SIDE_NAVIGATION.MOBILE_CLOSE_LABEL` translation.","isSignal":true},{"name":"navAriaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Optional override; falls back to `SIDE_NAVIGATION.NAV_ARIA_LABEL` translation.","isSignal":true},{"name":"isCollapsed","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"mainCtaClicked","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"navigate","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"accountMenuSelected","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"logoClicked","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"isCollapsedChange","eventType":"boolean","alias":null,"description":"Two-way binding change event for isCollapsed","isSignal":true}],"module":{"name":"SideNavigationComponentModule","imports":["A11yModule","CommonModule","UiTranslatePipe","ButtonComponentModule","AvatarComponentModule","IconComponentModule","LogoComponentModule","OverflowMenuComponentModule","SideNavigationComponent"],"exports":["SideNavigationComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[{"name":"NavigationCta","properties":[{"name":"label","type":"string","optional":false},{"name":"variant","type":"ButtonColor","optional":false},{"name":"icon","type":"IconName","optional":false}]},{"name":"NavigationItem","properties":[{"name":"id","type":"string","optional":false},{"name":"icon","type":"IconName","optional":false},{"name":"text","type":"string","optional":false},{"name":"url","type":"string","optional":false},{"name":"badgeOptions","type":"ButtonBadgeConfig","optional":true}]},{"name":"NavigationAccount","properties":[{"name":"name","type":"string","optional":false},{"name":"email","type":"string","optional":false},{"name":"menu","type":"OverflowMenuButtonsType[]","optional":false}]}],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<div class=\"mobile-navbar\">\n <ui-logo [allowResizing]=\"true\" [width]=\"150\" [type]=\"logoType.DEFAULT\" (click)=\"logoClicked.emit()\"></ui-logo>\n\n <ui-button\n data-testid=\"navigation-collapse-mobile\"\n data-test-role=\"navigation-collapse\"\n [applicationTheme]=\"applicationTheme()\"\n [variant]=\"'menuTrigger'\"\n [iconName]=\"isCollapsed() ? 'Menu-burger' : 'Close'\"\n [ariaLabel]=\"mobileToggleAriaLabelOverride() ?? (mobileToggleAriaKey() | uiTranslate | async) ?? ''\"\n [ariaExpanded]=\"!isCollapsed()\"\n [ariaControls]=\"drawerControlsId\"\n [tooltip]=\"mobileToggleAriaLabelOverride() ?? (mobileToggleAriaKey() | uiTranslate | async) ?? ''\"\n tooltipPosition=\"left\"\n (buttonClickEvent)=\"toggleCollapsed()\"\n class=\"navigation-collapse-button\"\n ></ui-button>\n</div>\n\n<div class=\"main-container\">\n <nav\n [id]=\"drawerControlsId\"\n role=\"navigation\"\n [attr.aria-label]=\"navAriaLabel() ?? (translationKey.NAV | uiTranslate | async)\"\n class=\"side-navigation-container\"\n [class.collapsed]=\"isCollapsed()\"\n [class.expanded]=\"!isCollapsed()\"\n [cdkTrapFocus]=\"trapFocus()\"\n [cdkTrapFocusAutoCapture]=\"trapFocus()\"\n >\n <div class=\"zone-logo-row\">\n <div class=\"header\" [class.collapsed]=\"isCollapsed()\">\n <ui-logo\n class=\"logo-brandmark\"\n [allowResizing]=\"true\"\n [width]=\"19\"\n [type]=\"logoType.PRIMARY_BRANDMARK_PINK\"\n (click)=\"logoClicked.emit()\"\n ></ui-logo>\n <ui-logo\n class=\"logo-lockup\"\n [allowResizing]=\"true\"\n [width]=\"136\"\n [type]=\"logoType.DEFAULT\"\n (click)=\"logoClicked.emit()\"\n ></ui-logo>\n\n <ui-button\n data-testid=\"navigation-collapse\"\n data-test-role=\"navigation-collapse\"\n [applicationTheme]=\"applicationTheme()\"\n [variant]=\"'menuTrigger'\"\n [iconName]=\"isCollapsed() ? 'Sidebar-open-in-line' : 'Sidebar-close-in-line'\"\n [ariaLabel]=\"toggleAriaLabelOverride() ?? (toggleAriaKey() | uiTranslate | async) ?? ''\"\n [ariaPressed]=\"isCollapsed()\"\n [tooltip]=\"toggleAriaLabelOverride() ?? (toggleAriaKey() | uiTranslate | async) ?? ''\"\n tooltipPosition=\"below\"\n (buttonClickEvent)=\"toggleCollapsed()\"\n [class.collapsed]=\"isCollapsed()\"\n class=\"navigation-collapse-button\"\n ></ui-button>\n </div>\n </div>\n\n <div class=\"zone-primary-nav\">\n @if (mainCta(); as cta) {\n <div class=\"main-cta\">\n <ui-button\n data-testid=\"main-cta\"\n data-test-role=\"main-cta\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [variant]=\"cta.variant\"\n iconPosition=\"left\"\n [iconName]=\"cta.icon\"\n [label]=\"isCollapsed() && !isExpandingOrCollapsing() ? '' : cta.label\"\n [ariaLabel]=\"cta.label\"\n [tooltip]=\"isCollapsed() ? cta.label : ''\"\n tooltipPosition=\"right\"\n (buttonClickEvent)=\"mainCtaClicked.emit()\"\n ></ui-button>\n </div>\n }\n\n <ul class=\"navigation-list\">\n @for (item of mainNavigation(); track item.id) {\n <li>\n <ng-container [ngTemplateOutlet]=\"navigationItem\" [ngTemplateOutletContext]=\"{ item }\"></ng-container>\n </li>\n }\n </ul>\n </div>\n\n <div class=\"zone-utility-nav\">\n @if (secondaryNavigation(); as secondary) {\n <ul class=\"navigation-list\">\n @for (item of secondary; track item.id) {\n <li>\n <ng-container [ngTemplateOutlet]=\"navigationItem\" [ngTemplateOutletContext]=\"{ item }\"></ng-container>\n </li>\n }\n </ul>\n }\n </div>\n\n @if (account() || contentTemplateExtendedRef() || contentTemplateCollapsedRef()) {\n <div class=\"zone-account\">\n @if (contentTemplateExtendedRef(); as extendedRef) {\n @if (!isCollapsed() && !isExpandingOrCollapsing()) {\n <ng-container [ngTemplateOutlet]=\"extendedRef\"></ng-container>\n }\n }\n\n @if (contentTemplateCollapsedRef(); as collapsedRef) {\n @if (isCollapsed() || isExpandingOrCollapsing()) {\n <ng-container [ngTemplateOutlet]=\"collapsedRef\"></ng-container>\n }\n }\n\n @if (account(); as accountValue) {\n <ui-overflow-menu\n [applicationTheme]=\"applicationTheme()\"\n [contentTemplateRef]=\"userInfo\"\n [buttons]=\"accountValue.menu\"\n [isDynamicMenu]=\"true\"\n [class.collapsed]=\"isCollapsed() && !isExpandingOrCollapsing()\"\n (selectItem)=\"onSelectMenuItem($event)\"\n [menuConfig]=\"overflowMenuConfig()\"\n ></ui-overflow-menu>\n }\n </div>\n }\n </nav>\n <div class=\"app-container\" [class.collapsed]=\"isCollapsed()\" [class.expanded]=\"!isCollapsed()\">\n @if (appContentTemplateRef(); as appRef) {\n <ng-container [ngTemplateOutlet]=\"appRef\"></ng-container>\n }\n @if (isMobileOrTablet() && !isCollapsed()) {\n <button\n type=\"button\"\n class=\"backdrop\"\n [attr.aria-label]=\"mobileCloseLabel() ?? (translationKey.MOBILE_CLOSE | uiTranslate | async) ?? ''\"\n (click)=\"toggleCollapsed()\"\n ></button>\n }\n </div>\n</div>\n\n<ng-template #navigationItem let-item=\"item\">\n @if (item) {\n <div class=\"menu-item\" [attr.aria-current]=\"activeRoutesMap().get(item.url) ? 'page' : null\">\n <ui-button\n [attr.data-testid]=\"item.id\"\n data-test-role=\"menu-item\"\n [applicationTheme]=\"applicationTheme()\"\n [fullWidth]=\"true\"\n [disabledScaleOnClick]=\"true\"\n [variant]=\"'menuCell'\"\n iconPosition=\"left\"\n [iconName]=\"item.icon\"\n [label]=\"isCollapsed() && !isExpandingOrCollapsing() ? '' : item.text\"\n (buttonClickEvent)=\"onNavigateClick(item.id)\"\n (keydown.Tab)=\"$event.stopPropagation()\"\n (keydown.shift.tab)=\"$event.stopPropagation()\"\n [ariaLabel]=\"item.text\"\n [class.active]=\"activeRoutesMap().get(item.url)\"\n [buttonBadgeConfig]=\"isCollapsed() && !isExpandingOrCollapsing() ? undefined : item?.badgeOptions\"\n [tooltip]=\"isCollapsed() ? item.text : undefined\"\n tooltipPosition=\"right\"\n [iconFilled]=\"!!activeRoutesMap().get(item.url)\"\n ></ui-button>\n </div>\n }\n</ng-template>\n\n<ng-template #userInfo>\n @if (account(); as accountValue) {\n <div class=\"user-info\">\n <ui-avatar [size]=\"isCollapsed() ? avatarSize.MEDIUM : avatarSize.SMALL\" [name]=\"accountValue.name\"></ui-avatar>\n @if (!isCollapsed()) {\n <span class=\"user-name\">{{ accountValue.name }}</span>\n <ui-icon class=\"user-chevron\" name=\"Arrow-chevron-right-in-line\" size=\"24\" aria-hidden=\"true\"></ui-icon>\n }\n </div>\n }\n</ng-template>\n","queryGuide":{"roles":["navigation-collapse","navigation","main-cta","menu-item"],"ariaAttributes":["aria-hidden","aria-label","aria-current"],"dataTestIds":["navigation-collapse-mobile","navigation-collapse","main-cta"],"materialComponents":[],"childUIComponents":["ui-logo","ui-button","ui-overflow-menu","ui-avatar","ui-icon"],"suggestedQueries":["screen.getByRole('navigation-collapse')","screen.getByRole('navigation')","screen.getByRole('main-cta')","screen.getByRole('menu-item')","screen.getByLabelText('label text')","screen.getByTestId('navigation-collapse-mobile')","screen.getByTestId('navigation-collapse')","screen.getByTestId('main-cta')"]},"filePath":"components/side-navigation/side-navigation.component.ts","supportsTheme":true,"lastModified":1786626756415.0479},"side-panel":{"name":"SidePanelComponent","selector":"ui-side-panel","category":"side-panel","description":"A full-height panel that slides in from the left or right, layering over the main content without navigating away. Used for candidate detail views, assessment preview, filtering options, and any context where the user needs a secondary workspace alongside the main page.","importPath":"@testgorilla/tgo-ui/components/side-panel","moduleName":"SidePanelComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SidePanelPosition","value":"'left' | 'right'"},{"name":"SidePanelSize","value":"'small' | 'large' | 'x-large'"}],"interfaces":[],"enums":[{"name":"SidePanelAnimationState","members":[{"name":"Open","value":"'open'"},{"name":"Closed","value":"'closed'"},{"name":"ClosedRight","value":"'closedRight'"},{"name":"ClosedLeft","value":"'closedLeft'"}]}]},"examples":[],"documentation":"A full-height panel that slides in from the left or right, layering over the main content without navigating away. Used for candidate detail views, assessment preview, filtering options, and any context where the user needs a secondary workspace alongside the main page.\n\n## When to use\n\n- **Focused content that shouldn't replace the page** — the user will return to the underlying view\n- **Multi-step flows** — use the Back Button variant for wizards within the panel (e.g. a setup wizard)\n- **Passing data in** — use the SIDE_PANEL_DATA injection token rather than global state\n\n## When NOT to use\n\n- **A full-page replacement** → if the content is complex enough to warrant its own URL and navigation history, use a page instead\n- **From within a Modal** → stacked overlays create confusing layering\n- **Brief confirmations or simple messages** → use a Snackbar or Confirm Dialog instead\n\n## How to use\n\nTo use the Side Panel component, you need to inject the `SidePanelService` in your component:\n\n```typescript\nimport { SidePanelService } from '@testgorilla/tgo-ui';\n\n@Component({\n selector: 'your-component',\n // ... other component metadata\n})\nexport class YourComponent {\n constructor(private sidePanelService: SidePanelService) {}\n\n // ...\n}\n```\n\nThe Side Panel component is used to display content in a panel that slides in from the side of the screen. You can control which side it appears from, whether it shows a header, back button, close button and set a title for the panel.\n\n### Opening a Side Panel\n\nTo open a side panel, use the `open` method of the `SidePanelService`:\n\n```typescript\nopenPanel(): void {\n this.sidePanelService.open(YourContentComponent, {\n position: 'right', // or 'left'\n showBackButton: true,\n title: 'Panel Title',\n showCloseButton: true,\n showHeader: true,\n data: { key: 'value' }, // Optional data to pass to the component\n });\n}\n```\n\nThe first parameter is the component that you want to display inside the side panel. The second parameter is a configuration object with the following properties:\n\n- `position`: Either 'left' or 'right', determining which side the panel slides in from\n- `showHeader`: Whether to show the header of the panel (defaults to true)\n- `showBackButton`: Whether to show a back button in the panel header\n- `title`: The title to display in the panel header\n- `showCloseButton`: Whether to show a close button in the panel header (defaults to true)\n- `data`: Optional data to pass to the component displayed in the side panel\n- `size`: The panel width — `'small'` (default, 600px), `'large'` (956px), or `'x-large'` (1200px). Each size fills the full page width below its breakpoint; `x-large` stays fixed at 1200px above the `x-large` (1280px) breakpoint and fills the full page width at and below it.\n\n### Creating a Content Component\n\nYour content component will be displayed inside the side panel. To close the panel from within your content component, you can inject the `SidePanelComponent` and call its `close` method:\n\n```typescript\nimport { SidePanelComponent } from '@testgorilla/tgo-ui';\n\n@Component({\n // ... component metadata\n})\nexport class YourContentComponent {\n constructor(private sidePanelComponent: SidePanelComponent) {}\n\n closePanel(): void {\n this.sidePanelComponent.close();\n }\n}\n```\n\n### Accessing Data in the Content Component\n\nIf you pass data to the side panel through the `data` property in the configuration, you can access it in your content component using the `SIDE_PANEL_DATA` injection token:\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { SidePanelComponent, SIDE_PANEL_DATA } from '@testgorilla/tgo-ui';\n\n@Component({\n selector: 'app-content',\n template: `\n <div>\n <h2>Data from parent:</h2>\n <pre>{{ data | json }}</pre>\n <button (click)=\"close()\">Close Panel</button>\n </div>\n `,\n})\nexport class ContentComponent {\n private sidePanelComponent = inject(SidePanelComponent);\n\n // Access the data passed to the side panel\n data = inject(SIDE_PANEL_DATA);\n\n close(): void {\n this.sidePanelComponent.close();\n }\n}\n```\n\n### Examples\n\n#### Basic Usage\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { SidePanelService } from '@testgorilla/tgo-ui';\n\n@Component({\n selector: 'app-demo',\n template: ` <button (click)=\"openSidePanel()\">Open Side Panel</button> `,\n})\nexport class DemoComponent {\n private sidePanelService = inject(SidePanelService);\n\n openSidePanel(): void {\n this.sidePanelService.open(ContentComponent, {\n position: 'right',\n showBackButton: false,\n title: 'Information Panel',\n });\n }\n}\n\n@Component({\n template: `\n <div style=\"padding: 16px;\">\n <h2>Panel Content</h2>\n <p>This is the content of the side panel.</p>\n <button (click)=\"close()\">Close</button>\n </div>\n `,\n standalone: true,\n})\nexport class ContentComponent {\n private sidePanelComponent = inject(SidePanelComponent);\n\n close(): void {\n this.sidePanelComponent.close();\n }\n}\n```\n\n#### With Back Button\n\n```typescript\nthis.sidePanelService.open(ContentComponent, {\n position: 'right',\n showBackButton: true,\n title: 'Panel with Back Button',\n});\n```\n\n#### Left-positioned Panel\n\n```typescript\nthis.sidePanelService.open(ContentComponent, {\n position: 'left',\n showBackButton: false,\n title: 'Left Side Panel',\n});\n```\n\n#### With Data Passed to Component\n\n```typescript\nthis.sidePanelService.open(ContentComponent, {\n position: 'right',\n showBackButton: false,\n title: 'Panel with Data',\n data: {\n userId: 123,\n userName: 'John Doe',\n items: [1, 2, 3],\n },\n});\n```\n\n#### Without Header\n\n```typescript\nthis.sidePanelService.open(ContentComponent, {\n position: 'right',\n showHeader: false,\n});\n```\n\n#### With Header but No Close Button\n\n```typescript\nthis.sidePanelService.open(ContentComponent, {\n position: 'right',\n title: 'Panel Title',\n showCloseButton: false,\n});\n```\n","whenToUse":["**Focused content that shouldn't replace the page** — the user will return to the underlying view","**Multi-step flows** — use the Back Button variant for wizards within the panel (e.g. a setup wizard)","**Passing data in** — use the SIDE_PANEL_DATA injection token rather than global state"],"whenNotToUse":["**A full-page replacement** → if the content is complex enough to warrant its own URL and navigation history, use a page instead","**From within a Modal** → stacked overlays create confusing layering","**Brief confirmations or simple messages** → use a Snackbar or Confirm Dialog instead"],"template":"<div\n class=\"side-panel-backdrop\"\n #sidePanelBackdrop\n tabindex=\"0\"\n (click)=\"close()\"\n (keydown.enter)=\"close()\"\n [@fadeInOut]=\"isOpen() ? 'open' : 'closed'\"\n></div>\n<div\n class=\"side-panel\"\n [class]=\"sidePanelConfig.panelClass\"\n [class.side-panel-small]=\"isSmall()\"\n [class.side-panel-large]=\"isLarge()\"\n [class.side-panel-x-large]=\"isXLarge()\"\n #sidePanel\n [@slideInOut]=\"animation()\"\n>\n @if (sidePanelConfig.showHeader) {\n <div class=\"side-panel-header\">\n <div class=\"side-panel-title-container\">\n @if (sidePanelConfig.showBackButton) {\n <ui-button\n class=\"side-panel-action\"\n [variant]=\"'icon-button'\"\n [iconName]=\"'Arrow-chevron-left-filled'\"\n (click)=\"close()\"\n [tooltip]=\"('COMMON.BACK' | uiTranslate | async)!\"\n size=\"medium\"\n ></ui-button>\n }\n <span class=\"side-panel-title h3 bold\">{{ sidePanelConfig.title }}</span>\n </div>\n @if (sidePanelConfig.showCloseButton) {\n <ui-button\n class=\"side-panel-action side-panel-action-close\"\n [variant]=\"'icon-button'\"\n [iconName]=\"'Close'\"\n [tooltip]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n (click)=\"close()\"\n size=\"medium\"\n ></ui-button>\n }\n </div>\n }\n\n <div class=\"side-panel-content\">\n <ng-container #sidePanelContainer></ng-container>\n </div>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-button"],"suggestedQueries":[]},"filePath":"components/side-panel/side-panel.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=19381-156162","lastModified":1786626756415.629},"side-sheet":{"name":"SideSheetComponent","selector":"ui-side-sheet","category":"side-sheet","description":"SideSheetComponent component","importPath":"@testgorilla/tgo-ui/components/side-sheet","moduleName":"SideSheetComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"position","type":"SideSheetPosition","defaultValue":"'end'","required":false,"alias":null,"transform":null,"description":"Set position. Defaults \"end\"","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[{"name":"openChange","eventType":"boolean","alias":null,"description":"","isSignal":false}],"module":{"name":"SideSheetComponentModule","imports":["CommonModule","IconComponentModule","ButtonComponentModule","MatTooltipModule","MatSidenavModule","UiTranslatePipe","LogoComponentModule","SideSheetComponent"],"exports":["SideSheetComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SideSheetPosition","value":"'start' | 'end'"}],"interfaces":[{"name":"SideSheetConfig","properties":[{"name":"componentRef","type":"Type<unknown> | ComponentRef<unknown>","optional":false},{"name":"inputs","type":"Record<string, unknown>","optional":true},{"name":"title","type":"string","optional":true},{"name":"showLogo","type":"boolean","optional":true},{"name":"showBackButton","type":"boolean","optional":true},{"name":"showTitleTooltip","type":"boolean","optional":true},{"name":"closed","type":"Observable<void>","optional":true}]}],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"<ng-container>\n <mat-drawer-container class=\"side-sheet-container\" [attr.theme]=\"applicationTheme\" [hasBackdrop]=\"true\">\n <mat-drawer\n (closedStart)=\"onOpenChange(false)\"\n (openedStart)=\"onOpenChange(true)\"\n #drawer\n [mode]=\"'over'\"\n [autoFocus]=\"false\"\n class=\"side-sheet-content\"\n [position]=\"position\"\n >\n <div class=\"header\">\n <div class=\"title\">\n @if (showBackButton) {\n <ui-button\n [variant]=\"'icon-button'\"\n [iconName]=\"applicationTheme === 'classic' ? 'Arrow_left' : 'Arrow-chevron-left-filled'\"\n [companyColor]=\"applicationTheme === 'classic' ? '#000' : '#242424'\"\n [applicationTheme]=\"applicationTheme\"\n [tooltip]=\"('COMMON.BACK' | uiTranslate | async)!\"\n (click)=\"onClose()\"\n ></ui-button>\n }\n @if (title) {\n <span [matTooltip]=\"showTitleTooltip ? title : ''\" [matTooltipClass]=\"applicationTheme\">{{ title }}</span>\n }\n @if (showLogo) {\n <ui-logo></ui-logo>\n }\n </div>\n <ui-button\n [variant]=\"'icon-button'\"\n [iconName]=\"'Close'\"\n [companyColor]=\"applicationTheme === 'classic' ? '#000' : '#242424'\"\n [applicationTheme]=\"applicationTheme\"\n [tooltip]=\"('COMMON.CLOSE' | uiTranslate | async)!\"\n (click)=\"onClose()\"\n ></ui-button>\n </div>\n <ng-container #container></ng-container>\n </mat-drawer>\n <mat-drawer-content>\n <ng-content></ng-content>\n </mat-drawer-content>\n </mat-drawer-container>\n</ng-container>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":["mat-drawer-container","mat-drawer","mat-drawer-content"],"childUIComponents":["ui-button","ui-logo"],"suggestedQueries":[]},"filePath":"components/side-sheet/side-sheet.component.ts","supportsTheme":true,"lastModified":1786626756416.1907},"skeleton":{"name":"SkeletonComponent","selector":"ui-skeleton","category":"skeleton","description":"Placeholder loading animations that mirror the shape of incoming content — lines, circles, or custom rectangles — to reduce perceived loading time. Maintains layout stability while data fetches, preventing the jarring shift from empty to populated. Use in tables, cards, and any content area that loads asynchronously.","importPath":"@testgorilla/tgo-ui/components/skeleton","moduleName":"SkeletonComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"count","type":"number | string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"isAiTheme","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"theme","type":"NgxSkeletonLoaderConfigTheme","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"appearance","type":"Appearance","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false}],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"Appearance","value":"NgxSkeletonLoaderConfig['appearance']"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"Placeholder loading animations that mirror the shape of incoming content — lines, circles, or custom rectangles — to reduce perceived loading time. Maintains layout stability while data fetches, preventing the jarring shift from empty to populated. Use in tables, cards, and any content area that loads asynchronously.\n\n## When to use\n\n- **Match the real content** — mirror the skeleton shape as closely as possible to what it replaces, a circular skeleton for an Avatar, line skeletons for text\n- **Lists and tables** → use the count prop to render multiple rows at once\n- **AI-powered surfaces** → use the AI Colors variant to signal that AI content is loading\n\n## When NOT to use\n\n- **User-triggered actions** → use a Spinner instead, Skeleton is for page-level async data loading, not button loading states\n- **Long waits** → don't show Skeleton for longer than ~3 seconds without switching to an error or empty state\n- **Over-engineered layouts** → approximate shapes are fine, don't fuss over pixel-perfect skeletons\n\n## How to use\n\nTo use the Skeleton component, you need to import the `SkeletonComponent` in your module:\n\n```typescript\nimport { SkeletonComponent } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SkeletonComponent,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-skeleton [count]=\"3\" [appearance]=\"'line'\" [theme]=\"{ height: '24px' }\" applicationTheme=\"light\"> </ui-skeleton>\n```\n\nThe component creates placeholder loading animations and supports various shapes and sizes.\n","whenToUse":["**Match the real content** — mirror the skeleton shape as closely as possible to what it replaces, a circular skeleton for an Avatar, line skeletons for text","**Lists and tables** → use the count prop to render multiple rows at once","**AI-powered surfaces** → use the AI Colors variant to signal that AI content is loading"],"whenNotToUse":["**User-triggered actions** → use a Spinner instead, Skeleton is for page-level async data loading, not button loading states","**Long waits** → don't show Skeleton for longer than ~3 seconds without switching to an error or empty state","**Over-engineered layouts** → approximate shapes are fine, don't fuss over pixel-perfect skeletons"],"template":"<ngx-skeleton-loader\n [class.skeleton-ai]=\"isAiTheme\"\n [count]=\"count\"\n [theme]=\"currentTheme$ | async\"\n [appearance]=\"(appearance$ | async) || 'line'\"\n></ngx-skeleton-loader>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":[]},"filePath":"components/skeleton/skeleton.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=23491-159461","lastModified":1786626756416.5},"slider":{"name":"SliderComponent","selector":"ui-slider","category":"slider","description":"An interactive range control for selecting a single value or a min/max range on a continuous scale. Supports decimal values, custom steps, percentage display, thousands separators, and constrained input fields. Used in assessment scoring configuration, salary range filters, and candidate screening criteria.","importPath":"@testgorilla/tgo-ui/components/slider","moduleName":"SliderComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"max","type":"number","defaultValue":"100","required":false,"alias":null,"transform":null,"description":"The maximum value of the slider.","isSignal":false},{"name":"min","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The minimum value of the slider.","isSignal":false},{"name":"percentage","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A flag indicating whether the slider displays values as percentages.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A flag indicating whether the slider is disabled.","isSignal":false},{"name":"sliderStart","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The starting value of the slider.","isSignal":false},{"name":"sliderEnd","type":"number","defaultValue":"100","required":false,"alias":null,"transform":null,"description":"The ending value of the slider.","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The label for the slider.","isSignal":false},{"name":"showInputs","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Show slider inputs.","isSignal":false},{"name":"step","type":"number","defaultValue":"1","required":false,"alias":null,"transform":null,"description":"Step number.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"variant","type":"SliderVariant","defaultValue":"'range'","required":false,"alias":null,"transform":null,"description":"Defines the variant slider","isSignal":false},{"name":"useDecimal","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Controls whether decimal values are allowed in the input fields.\nWhen true, inputs accept decimal numbers (up to 2 decimal places).\nWhen false, inputs only accept whole numbers.","isSignal":true},{"name":"useThousandsSeparator","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Controls whether numbers are displayed with thousands separators.\nWhen true, displays numbers like \"100,000\" instead of \"100000\".\nThis is independent of useDecimal - you can have thousands separators\nwithout decimals, or decimals without thousands separators.","isSignal":true},{"name":"minLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"maxLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"inputWidth","type":"number","defaultValue":"80","required":false,"alias":null,"transform":null,"description":"","isSignal":true}],"outputs":[{"name":"sliderValueChange","eventType":"SliderValue","alias":null,"description":"","isSignal":false}],"module":{"name":"SliderComponentModule","imports":["SliderComponent"],"exports":["SliderComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SliderVariant","value":"'basic' | 'range'"}],"interfaces":[{"name":"SliderValue","properties":[{"name":"start","type":"number","optional":false},{"name":"end","type":"number","optional":false}]}],"enums":[]},"examples":[],"documentation":"An interactive range control for selecting a single value or a min/max range on a continuous scale. Supports decimal values, custom steps, percentage display, thousands separators, and constrained input fields. Used in assessment scoring configuration, salary range filters, and candidate screening criteria.\n\n## When to use\n\n- **Min/max filtering** — use the range variant (sliderStart + sliderEnd) for ranges such as years of experience or salary band\n- **Meaningful bounds** — always set min and max bounds and show them as labels so users understand the full range\n- **Percentage values** — use the Percentage variant when the value is naturally expressed as a percentage\n\n## When NOT to use\n\n- **Discrete selections with only a few options** → a Dropdown or Radio Button group is clearer\n- **Precise value entry** → don't use the Slider without the input fields, as the drag handle alone is imprecise, especially on touch\n- **Confusing step values** → don't set a step that produces a confusing number of stops\n\n## How to use\n\nTo use the Slider component, you need to import the `SliderComponentModule` in your module:\n\n```typescript\nimport { SliderComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SliderComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-slider\n [min]=\"0\"\n [max]=\"100\"\n [sliderStart]=\"25\"\n [sliderEnd]=\"75\"\n applicationTheme=\"light\"\n (valueChange)=\"onValueChange($event)\"\n>\n</ui-slider>\n```\n\nThe slider supports single value or range selection, custom steps, decimal values, and percentage display.\n","whenToUse":["**Min/max filtering** — use the range variant (sliderStart + sliderEnd) for ranges such as years of experience or salary band","**Meaningful bounds** — always set min and max bounds and show them as labels so users understand the full range","**Percentage values** — use the Percentage variant when the value is naturally expressed as a percentage"],"whenNotToUse":["**Discrete selections with only a few options** → a Dropdown or Radio Button group is clearer","**Precise value entry** → don't use the Slider without the input fields, as the drag handle alone is imprecise, especially on touch","**Confusing step values** → don't set a step that produces a confusing number of stops"],"template":"<ng-container>\n <div\n class=\"slider-container\"\n [attr.theme]=\"applicationTheme\"\n [ngClass]=\"{ disabled: disabled, error: hasError && touchedControlName }\"\n >\n @if (label) {\n <div class=\"slider-label\">{{ label }}</div>\n }\n\n <div class=\"slider\">\n @if (!isBasicSlider) {\n <div class=\"min-value\">\n <span>\n @if (useThousandsSeparator()) {\n {{ min | number }}\n } @else {\n {{ min }}\n }\n @if (percentage) {\n %\n }\n </span>\n </div>\n }\n <mat-slider\n [disableRipple]=\"applicationTheme !== 'classic'\"\n #sliderElement\n discrete\n [disabled]=\"disabled\"\n [max]=\"max\"\n [min]=\"min\"\n [step]=\"step\"\n (mouseenter)=\"sliderElement.discrete = true\"\n (mouseleave)=\"sliderElement.discrete = false\"\n [displayWith]=\"formatLabel\"\n >\n @if (isBasicSlider) {\n <input\n data-testid=\"slider-thumb\"\n [disabled]=\"disabled\"\n (valueChange)=\"onValueChange($event)\"\n [(ngModel)]=\"sliderStart\"\n matSliderThumb\n />\n } @else {\n <input\n data-testid=\"slider-start-thumb\"\n [disabled]=\"disabled\"\n (valueChange)=\"onValueChange()\"\n [(ngModel)]=\"sliderStart\"\n matSliderStartThumb\n />\n <input\n data-testid=\"slider-end-thumb\"\n [disabled]=\"disabled\"\n (valueChange)=\"onValueChange()\"\n [(ngModel)]=\"sliderEnd\"\n matSliderEndThumb\n />\n }\n </mat-slider>\n @if (!isBasicSlider) {\n <div class=\"max-value\">\n <span>\n @if (useThousandsSeparator()) {\n {{ max | number }}\n } @else {\n {{ max }}\n }\n @if (percentage) {\n %\n }\n </span>\n </div>\n }\n </div>\n\n @if (showInputs && !isBasicSlider) {\n <div class=\"slider-input\">\n <mat-form-field [appearance]=\"'outline'\" [ngStyle]=\"{ width: inputWidth() + 'px' }\">\n @if (minLabel()) {\n <mat-label>\n {{ minLabel() }}\n @if (percentage) {\n <span>%</span>\n }\n </mat-label>\n } @else {\n <mat-label>\n {{ 'COMMON.MIN' | uiTranslate | async }}\n @if (percentage) {\n <span>%</span>\n }\n </mat-label>\n }\n @if (useDecimal()) {\n <input\n data-testid=\"slider.input-start\"\n matInput\n [type]=\"'text'\"\n [(ngModel)]=\"sliderStart\"\n [disabled]=\"disabled\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur('start')\"\n (keydown.enter)=\"onInputBlur('start')\"\n mask=\"separator.2\"\n [thousandSeparator]=\"useThousandsSeparator() ? ',' : ''\"\n />\n } @else {\n <input\n data-testid=\"slider.input-start\"\n matInput\n [type]=\"'text'\"\n [(ngModel)]=\"sliderStart\"\n [disabled]=\"disabled\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur('start')\"\n (keydown.enter)=\"onInputBlur('start')\"\n mask=\"separator.0\"\n [thousandSeparator]=\"useThousandsSeparator() ? ',' : ''\"\n />\n }\n </mat-form-field>\n <mat-form-field [appearance]=\"'outline'\" [ngStyle]=\"{ width: inputWidth() + 'px' }\">\n @if (maxLabel()) {\n <mat-label>\n {{ maxLabel() }}\n @if (percentage) {\n <span>%</span>\n }\n </mat-label>\n } @else {\n <mat-label>\n {{ 'COMMON.MAX' | uiTranslate | async }}\n @if (percentage) {\n <span>%</span>\n }\n </mat-label>\n }\n @if (useDecimal()) {\n <input\n matInput\n [disabled]=\"disabled\"\n [type]=\"'text'\"\n [(ngModel)]=\"sliderEnd\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur('end')\"\n (keydown.enter)=\"onInputBlur('end')\"\n data-testid=\"slider.input-end\"\n mask=\"separator.2\"\n [thousandSeparator]=\"useThousandsSeparator() ? ',' : ''\"\n />\n } @else {\n <input\n matInput\n [disabled]=\"disabled\"\n [type]=\"'text'\"\n [(ngModel)]=\"sliderEnd\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur('end')\"\n (keydown.enter)=\"onInputBlur('end')\"\n mask=\"separator.0\"\n [thousandSeparator]=\"useThousandsSeparator() ? ',' : ''\"\n data-testid=\"slider.input-end\"\n />\n }\n </mat-form-field>\n </div>\n }\n\n @if (hasError && touchedControlName) {\n <mat-error class=\"slider-errors\">\n @if (touchedControlName === 'start' && sliderStart > sliderEnd) {\n <div class=\"validation-error\">\n <ui-icon [name]=\"'Info'\"></ui-icon>\n <span>{{\n translationContext + 'MIN_MORE_THAN_MAX' | uiTranslate: { min: sliderStart, max: sliderEnd } | async\n }}</span>\n </div>\n }\n @if (touchedControlName === 'end' && sliderStart > sliderEnd) {\n <div class=\"validation-error\">\n <ui-icon [name]=\"'Info'\"></ui-icon>\n <span>{{\n translationContext + 'MAX_LESS_THAN_MIN' | uiTranslate: { min: sliderStart, max: sliderEnd } | async\n }}</span>\n </div>\n }\n @if (sliderStart < min) {\n <div class=\"validation-error\">\n <ui-icon [name]=\"'Info'\"></ui-icon>\n <span>{{ 'COMMON.MIN' | uiTranslate | async }} {{ min }}</span>\n </div>\n }\n @if (sliderEnd > max) {\n <div class=\"validation-error\">\n <ui-icon [name]=\"'Info'\"></ui-icon>\n <span>{{ 'COMMON.MAX' | uiTranslate | async }} {{ max }}</span>\n </div>\n }\n </mat-error>\n }\n </div>\n</ng-container>\n","queryGuide":{"roles":["slider"],"ariaAttributes":[],"dataTestIds":["slider-thumb","slider-start-thumb","slider-end-thumb","slider.input-start","slider.input-end"],"materialComponents":["mat-slider","mat-form-field","mat-label","mat-error"],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('slider')","screen.getByTestId('slider-thumb')","screen.getByTestId('slider-start-thumb')","screen.getByTestId('slider-end-thumb')","screen.getByTestId('slider.input-start')","screen.getByTestId('slider.input-end')"]},"filePath":"components/slider/slider.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2001-13650","lastModified":1786626756417.3875},"snackbar":{"name":"SnackbarComponent","selector":"ui-snackbar","category":"bars","description":"A transient toast notification that appears at the bottom of the screen to confirm that a user's action succeeded, failed, or is in progress. Auto-dismisses after a short duration. Used after saving settings, sending an invitation, publishing an assessment, or undoing a delete action.","importPath":"@testgorilla/tgo-ui/components/snackbar","moduleName":"SnackbarComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"snackbarType","type":"SnackbarType","defaultValue":"'success'","required":false,"alias":null,"transform":null,"description":"Background color of the button while in active state","isSignal":false},{"name":"message","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Snackbar message","isSignal":false},{"name":"seconds","type":"number","defaultValue":"5","required":false,"alias":null,"transform":null,"description":"Set the time that the snackbar will be visible","isSignal":false},{"name":"includeDismissButton","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Include dismmiss button","isSignal":false},{"name":"fixed","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Static or fixed position","isSignal":false},{"name":"linkText","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Link text that will appended at the end of message","isSignal":false},{"name":"linkUrl","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Link url","isSignal":false},{"name":"linkTarget","type":"LinkTargetType","defaultValue":"'_blank'","required":false,"alias":null,"transform":null,"description":"Link target","isSignal":false},{"name":"variant","type":"SnackbarVariant","defaultValue":"'desktop'","required":false,"alias":null,"transform":null,"description":"The variant to be used","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"closeButtonTooltip","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Close button tooltip","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"html","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Snackbar message from html","isSignal":false}],"outputs":[],"module":{"name":"SnackbarComponentModule","imports":["CommonModule","ButtonComponentModule","IconComponentModule","UiTranslatePipe","MatSnackBarModule","MatTooltipModule","SnackbarComponent"],"exports":["SnackbarComponent"],"declarations":[],"providers":["SnackbarService"]},"types":{"typeAliases":[{"name":"SnackbarType","value":"'success' | 'error' | 'info' | 'warning'"},{"name":"SnackbarVariant","value":"'desktop' | 'mobile'"}],"interfaces":[{"name":"SnackbarLink","properties":[{"name":"text","type":"string","optional":false},{"name":"url","type":"string","optional":false},{"name":"action","type":"() => void | null","optional":false}]}],"enums":[]},"examples":[],"documentation":"A transient toast notification that appears at the bottom of the screen to confirm that a user's action succeeded, failed, or is in progress. Auto-dismisses after a short duration. Used after saving settings, sending an invitation, publishing an assessment, or undoing a delete action.\n\n## When to use\n\n- **Immediate feedback** — trigger Snackbar right after the user's action completes so it feels like direct feedback\n- **Reversible operations** — include an undo or action link (e.g. 'Assessment archived. Undo')\n- **Genuine failures only** — use the Error variant when the action actually failed, not for warnings about future risk\n\n## When NOT to use\n\n- **Multiple Snackbars at once** → queue them and display sequentially rather than showing more than one at a time\n- **Workflow-blocking critical errors** → use an Alert Banner or inline error message instead\n- **Very short durations** → don't set auto-dismiss below 4 seconds; users need time to read the message\n\n## How to use\n\nTo use the Snackbar component, you need to import the `SnackbarComponentModule` in your module:\n\n```typescript\nimport { SnackbarComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SnackbarComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThe Snackbar is usually used via the service:\n\n```typescript\nimport { SnackbarService } from '@testgorilla/tgo-ui';\n\n// In your component\nconstructor(private snackbarService: SnackbarService) {}\n\nshowSuccessMessage() {\n this.snackbarService.show({\n message: 'Success message',\n snackbarType: 'success',\n seconds: 5\n });\n}\n```\n\nYou can also use the component directly in your templates:\n\n```html\n<ui-snackbar\n message=\"This is a snackbar message\"\n snackbarType=\"info\"\n [seconds]=\"10\"\n [includeDismissButton]=\"true\"\n applicationTheme=\"light\"\n>\n</ui-snackbar>\n```\n\nThe snackbar component is used to display brief messages to users.\n","whenToUse":["**Immediate feedback** — trigger Snackbar right after the user's action completes so it feels like direct feedback","**Reversible operations** — include an undo or action link (e.g. 'Assessment archived. Undo')","**Genuine failures only** — use the Error variant when the action actually failed, not for warnings about future risk"],"whenNotToUse":["**Multiple Snackbars at once** → queue them and display sequentially rather than showing more than one at a time","**Workflow-blocking critical errors** → use an Alert Banner or inline error message instead","**Very short durations** → don't set auto-dismiss below 4 seconds; users need time to read the message"],"template":"<ng-container>\n @if (visible) {\n <div\n [ngClass]=\"snackbarType + ' ' + position\"\n [@openClose]\n class=\"snackbar-container\"\n [attr.theme]=\"applicationTheme\"\n [class.mobile]=\"variant === 'mobile'\"\n [class.desktop]=\"variant === 'desktop'\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n data-testid=\"snackbar-wrapper\"\n [attr.data-test-snackbar-type]=\"snackbarType\"\n [attr.aria-describedby]=\"'describedby'\"\n role=\"alert\"\n [attr.aria-labelledby]=\"'labelledby'\"\n >\n <span style=\"position: absolute; left: -9999px\" id=\"labelledby\">{{\n 'ALERT_BANNER.' + (snackbarType | uppercase) | uiTranslate | async\n }}</span>\n <span style=\"position: absolute; left: -9999px\"\n >{{ message }}\n @if (!!linkText) {\n <a [target]=\"linkTarget\" [href]=\"linkUrl\" (click)=\"onLinkClick($event)\">{{ linkText }}</a>\n }\n </span>\n <span [style.display]=\"'none'\" id=\"describedby\">{{ ariaDescribedby }}</span>\n <div\n aria-hidden=\"true\"\n class=\"snackbar-text\"\n [matTooltip]=\"message.length > maxCharacters ? message : ''\"\n data-testid=\"snackbar-content\"\n >\n @if (applicationTheme === 'classic') {\n <ui-icon [name]=\"iconName\" size=\"24\"></ui-icon>\n }\n {{ message.length > maxCharacters ? (message | slice: 0 : maxCharacters) + '..' : message }}\n @if (safeHtml) {\n <div [innerHTML]=\"safeHtml\"></div>\n }\n @if (!!linkText) {\n <a [target]=\"linkTarget\" [href]=\"linkUrl\" (click)=\"onLinkClick($event)\">{{ linkText }}</a>\n }\n </div>\n @if (includeDismissButton && applicationTheme === 'classic') {\n <ui-button\n variant=\"text\"\n [label]=\"(translationContext + 'DISMISS' | uiTranslate | async)!\"\n [applicationTheme]=\"applicationTheme !== 'classic' ? 'light' : 'classic'\"\n (buttonClickEvent)=\"dismissClick()\"\n data-testid=\"snackbar-dismiss-button\"\n ></ui-button>\n }\n @if (includeDismissButton && applicationTheme !== 'classic') {\n <ui-button\n [variant]=\"'icon-button'\"\n [applicationTheme]=\"'light'\"\n [iconName]=\"'Close-in-line'\"\n [size]=\"'small'\"\n [tooltip]=\"(closeButtonTooltip || ('COMMON.CLOSE' | uiTranslate | async))!\"\n (click)=\"dismissClick()\"\n data-testid=\"snackbar-dismiss-button\"\n ></ui-button>\n }\n </div>\n }\n</ng-container>\n","queryGuide":{"roles":["alert"],"ariaAttributes":["aria-hidden","aria-describedby","aria-labelledby"],"dataTestIds":["snackbar-wrapper","snackbar-content","snackbar-dismiss-button"],"materialComponents":[],"childUIComponents":["ui-icon","ui-button"],"suggestedQueries":["screen.getByRole('alert')","screen.getByTestId('snackbar-wrapper')","screen.getByTestId('snackbar-content')","screen.getByTestId('snackbar-dismiss-button')"]},"filePath":"components/snackbar/snackbar.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=1018-10233","lastModified":1786626756417.82},"spider-chart":{"name":"SpiderChartComponent","selector":"ui-spider-chart","category":"charts","description":"A radar chart that plots a candidate's performance across multiple skill dimensions on a single visualisation. Each axis represents a different dimension, and the shaded area shows the candidate's overall profile shape. Used in multi-dimensional test results to communicate balance and relative strengths.","importPath":"@testgorilla/tgo-ui/components/spider-chart","moduleName":"SpiderChartComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"SpiderChartSize","defaultValue":"'large'","required":false,"alias":null,"transform":null,"description":"The size of the spider chart.","isSignal":false},{"name":"spiderChartData","type":"SpiderChartData","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The data to be used for the spider chart.","isSignal":false},{"name":"stepSize","type":"number","defaultValue":"20","required":false,"alias":null,"transform":null,"description":"The step size for the spider chart.","isSignal":false},{"name":"ariaLabel","type":"unknown","defaultValue":"'Radar chart. Press tab to navigate through the chart.'","required":false,"alias":null,"transform":null,"description":"The aria label for the spider chart.","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the spider chart is loading.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false}],"outputs":[],"module":{"name":"SpiderChartComponentModule","imports":["NgClass","UiTranslatePipe","BaseChartDirective","MatTooltipModule","MemoizeFuncPipe","SpinnerComponentModule","CommonModule","SpiderChartComponent"],"exports":["SpiderChartComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SpiderChartSize","value":"'large' | 'small'"},{"name":"SpiderChartData","value":"{\n labels: string[];\n labelDescription: string[];\n plotData: [PlotData, PlotData] | [PlotData]; // Current implementation supports max 2 datasets.\n}"},{"name":"PlotData","value":"{\n databaseLabel: string;\n data: number[];\n}"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A radar chart that plots a candidate's performance across multiple skill dimensions on a single visualisation. Each axis represents a different dimension, and the shaded area shows the candidate's overall profile shape. Used in multi-dimensional test results to communicate balance and relative strengths.\n\n## When to use\n\n- **Clearly labelled axes** — name every axis with its dimension; the chart is uninterpretable without them\n- **Filled opacity** — use a shaded area, not just an outline, so the candidate's profile shape reads at a glance\n- **Paired with a tabular breakdown** of scores for users who need precise values\n\n## When NOT to use\n\n- **Fewer than 4 dimensions** → a bar chart or score list is clearer at low dimension counts\n- **More than 2 candidates' profiles** → beyond that, the overlap destroys legibility\n- **As the only result visualisation** → some hiring managers find radar charts harder to interpret\n","whenToUse":["**Clearly labelled axes** — name every axis with its dimension; the chart is uninterpretable without them","**Filled opacity** — use a shaded area, not just an outline, so the candidate's profile shape reads at a glance","**Paired with a tabular breakdown** of scores for users who need precise values"],"whenNotToUse":["**Fewer than 4 dimensions** → a bar chart or score list is clearer at low dimension counts","**More than 2 candidates' profiles** → beyond that, the overlap destroys legibility","**As the only result visualisation** → some hiring managers find radar charts harder to interpret"],"template":"<div class=\"spider-chart-container\" tabindex=\"0\" [attr.aria-label]=\"ariaLabel\">\n <div\n class=\"spacing-container\"\n [ngClass]=\"{ 'small-spacing': radarChartData.labels && radarChartData.labels.length % 2 === 1 && !loading }\"\n >\n <div\n class=\"chart-container\"\n [style.width]=\"size === 'large' ? '280px' : '200px'\"\n [style.height]=\"size === 'large' ? '280px' : '200px'\"\n >\n @if (!loading) {\n <canvas baseChart [data]=\"radarChartData\" [options]=\"radarChartOptions\" type=\"radar\" class=\"chart\"> </canvas>\n } @else {\n <canvas baseChart [data]=\"loadingChartData\" [options]=\"loadingChartOptions\" type=\"radar\" class=\"chart\">\n </canvas>\n <ui-spinner></ui-spinner>\n }\n\n @if (labelPositionsVisible) {\n <div class=\"overlay-container\" [style.visibility]=\"labelStylesCached ? 'visible' : 'hidden'\">\n @for (label of spiderChartData.labels; track label) {\n <div\n class=\"label-container\"\n #labelContainer\n [matTooltip]=\"spiderChartData.labelDescription[$index]\"\n [tabindex]=\"$index === 0 ? '0' : '-1'\"\n (keydown)=\"onLabelKeyDown($event, $index)\"\n [attr.aria-label]=\"spiderChartData.labelDescription[$index]\"\n [style.left]=\"labelStyles[label]?.left\"\n [style.bottom]=\"labelStyles[label]?.bottom\"\n >\n @if (!(label.length > 20 && size === 'large' && spiderChartData.labels.length < 9)) {\n <div class=\"label\" [style.max-width]=\"size === 'large' ? '135px' : '100px'\">\n {{ label }}\n </div>\n } @else {\n <div>\n <div class=\"label\" [style.max-width]=\"size === 'large' ? '135px' : '100px'\">\n {{ label | memoizeFunc: getFirstLine }}\n </div>\n <div class=\"label\" [style.max-width]=\"size === 'large' ? '135px' : '100px'\">\n {{ label | memoizeFunc: getSecondLine : this }}\n </div>\n </div>\n }\n <span class=\"value\">{{ spiderChartData.plotData[0].data[$index] }}</span>\n </div>\n <span\n class=\"fake-data-points\"\n #fakeDataPoints\n [tabindex]=\"focusedLabelIndex === $index ? '0' : '-1'\"\n (focus)=\"$index === focusedLabelIndex ? showTooltipForDataPoint($index) : null\"\n (keydown)=\"onFakeDataPointsKeyDown($event, $index)\"\n >\n </span>\n @if (radarChartData.datasets.length > 1) {\n <span\n class=\"fake-data-points-secondary\"\n #fakeDataPointsSecondary\n [tabindex]=\"focusedFakeDataPointIndex === $index ? '0' : '-1'\"\n (focus)=\"$index === focusedFakeDataPointIndex ? showTooltipForDataPoint($index, true) : null\"\n (keydown)=\"onFakeDataPointsKeyDown($event, $index, true)\"\n >\n </span>\n }\n }\n </div>\n }\n </div>\n </div>\n @if (radarChartData.datasets.length > 0) {\n <div class=\"legend-container\">\n @for (dataset of radarChartData.datasets; track dataset.label) {\n <div\n class=\"legend-item\"\n (click)=\"toggleDatasetVisibility($index)\"\n [tabindex]=\"$index === 0 ? '0' : '-1'\"\n role=\"button\"\n [attr.aria-pressed]=\"isDatasetVisible($index) ? false : true\"\n [attr.aria-label]=\"\n isDatasetVisible($index)\n ? (translationContext + 'DATASET_VISIBLE' | uiTranslate: { dataset: dataset.label } | async)\n : (translationContext + 'DATASET_HIDDEN' | uiTranslate: { dataset: dataset.label } | async)\n \"\n (keydown)=\"onLegendItemKeyDown($event, $index)\"\n #legendItem\n >\n <span class=\"legend-line\" [ngClass]=\"{ 'solid-line': $index === 0, 'dashed-line': $index === 1 }\"></span>\n <span class=\"legend-label\" [style.text-decoration]=\"isDatasetVisible($index) ? 'none' : 'line-through'\">\n {{ dataset.label }}\n </span>\n </div>\n }\n </div>\n }\n</div>\n","queryGuide":{"roles":["button"],"ariaAttributes":["aria-label","aria-pressed"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-spinner"],"suggestedQueries":["screen.getByRole('button')","screen.getByLabelText('label text')"]},"filePath":"components/spider-chart/spider-chart.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=13198-19229","lastModified":1786626756418.382},"spinner":{"name":"SpinnerComponent","selector":"ui-spinner","category":"spinner","description":"A circular loading indicator for operations with an unknown duration. Use it for in-place loading — a button submitting, a section refreshing, a component waiting for data — rather than for full-page loads. Available in multiple sizes and with an optional loading text label.","importPath":"@testgorilla/tgo-ui/components/spinner","moduleName":"SpinnerComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"size","type":"SpinnerSize","defaultValue":"'large'","required":false,"alias":null,"transform":null,"description":"The size variant of the spinner. Can be 'small' or 'large'.\nDefaults to 'large'.","isSignal":false},{"name":"isLoader","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"Set position fixed on page\nDefaults to 'true'.","isSignal":false},{"name":"text","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"The text to display below the spinner.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"iconName","type":"IconName","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Custom Icon name","isSignal":false},{"name":"iconSize","type":"IconSize","defaultValue":"'24'","required":false,"alias":null,"transform":null,"description":"Custom Icon size","isSignal":false}],"outputs":[],"module":{"name":"SpinnerComponentModule","imports":["NgClass","UiTranslatePipe","AsyncPipe","IconComponentModule","SpinnerComponent"],"exports":["SpinnerComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"SpinnerSize","value":"'large' | 'small'"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"A circular loading indicator for operations with an unknown duration. Use it for in-place loading — a button submitting, a section refreshing, a component waiting for data — rather than for full-page loads. Available in multiple sizes and with an optional loading text label.\n\n## When to use\n\n- **Small size** inside buttons and tight containers; use medium as the default in cards and sections\n- **With Text** in contexts where it's unclear what is loading ('Saving your assessment…')\n- **Centre the spinner** within the container it represents — it should occupy the same space as the content it replaces\n\n## When NOT to use\n\n- **Known duration or progress** → use ProgressBar instead when the operation has a known duration or progress percentage\n- **No timeout or fallback** → don't show a Spinner indefinitely without a timeout or error fallback\n- **Partial content updates** → don't show a full-page Spinner; scope it to the loading region only\n\n## How to use\n\nTo use the Spinner component, you need to import the `SpinnerComponentModule` in your module:\n\n```typescript\nimport { SpinnerComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n SpinnerComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-spinner size=\"medium\" [text]=\"'Loading...'\" [isLoader]=\"true\" applicationTheme=\"light\"> </ui-spinner>\n```\n\nThe spinner can be used to indicate loading states throughout the application.\n","whenToUse":["**Small size** inside buttons and tight containers; use medium as the default in cards and sections","**With Text** in contexts where it's unclear what is loading ('Saving your assessment…')","**Centre the spinner** within the container it represents — it should occupy the same space as the content it replaces"],"whenNotToUse":["**Known duration or progress** → use ProgressBar instead when the operation has a known duration or progress percentage","**No timeout or fallback** → don't show a Spinner indefinitely without a timeout or error fallback","**Partial content updates** → don't show a full-page Spinner; scope it to the loading region only"],"template":"<div\n class=\"spinner-container\"\n [ngClass]=\"classes\"\n [attr.theme]=\"applicationTheme\"\n [attr.aria-label]=\"'COMMON.LOADING' | uiTranslate | async\"\n role=\"alert\"\n>\n @if (!iconName) {\n <svg class=\"spinner\" viewBox=\"0 0 50 50\">\n <circle class=\"path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\"></circle>\n </svg>\n } @else {\n <ui-icon [name]=\"iconName\" [size]=\"iconSize\"></ui-icon>\n }\n\n @if (text) {\n <h3 class=\"spinner-text\">{{ text }}</h3>\n }\n</div>\n","queryGuide":{"roles":["alert"],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByRole('alert')","screen.getByLabelText('label text')"]},"filePath":"components/spinner/spinner.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=695-32677","lastModified":1786626756418.6614},"stepper":{"name":"StepperComponent","selector":"ui-stepper","category":"stepper","description":"A step-by-step progress indicator for multi-stage workflows. Shows the user's current position, completed steps, and remaining steps at a glance. Ships two visual variants tied to specific product flows: the default `primary` belongs to the candidate test-taking experience, while `secondary` is the newer company-side style used in assessment/job creation wizards.","importPath":"@testgorilla/tgo-ui/components/stepper","moduleName":"StepperComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"lastStepFinalIcon","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input property indicating whether the final icon should be displayed.\nDefault value is false.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"position","type":"StepperPosition","defaultValue":"'horizontal'","required":false,"alias":null,"transform":null,"description":"Defines the stepper position","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"stepperVariant","type":"StepperVariation","defaultValue":"'primary'","required":false,"alias":null,"transform":null,"description":"Selects the visual variant. 'primary' (the default) is the candidate\ntest-taking-flow style; 'secondary' is the newer company-side style\nused in assessment/job creation wizards. Don't use the default in\ncompany-side wizards.","isSignal":false},{"name":"setFirstStepAutoSelect","type":"boolean","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Auto selects first step as visited\nDefaults to true","isSignal":false},{"name":"steps","type":"Step[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input property to set the steps for the stepper.\nThe first step will be marked as visited automatically.","isSignal":false},{"name":"setSelectedIndex","type":"number","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input property to set the selected index of the stepper.\nDefault value is 0.","isSignal":false}],"outputs":[{"name":"selectionChange","eventType":"{ selectedIndex: number; previousIndex: number; step: Step }","alias":null,"description":"","isSignal":false}],"module":{"name":"StepperComponentModule","imports":["CommonModule","IconComponentModule","StepComponentModule","MatProgressSpinnerModule","RadialProgressComponentModule","UiTranslatePipe","MatInputModule","StepperComponent","StepLineElementDirective"],"exports":["StepperComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"StepperPosition","value":"'horizontal' | 'vertical'"},{"name":"StepperVariation","value":"'primary' | 'secondary'"}],"interfaces":[{"name":"Step","properties":[{"name":"label","type":"string","optional":false},{"name":"isVisited","type":"boolean","optional":true},{"name":"iconName","type":"IconName","optional":true},{"name":"disabled","type":"boolean","optional":true},{"name":"submitted","type":"boolean","optional":true},{"name":"showIconWhenSelected","type":"boolean","optional":true},{"name":"htmlDescription","type":"HtmlDescription[]","optional":true},{"name":"boldLabel","type":"boolean","optional":true},{"name":"descriptionTpl","type":"TemplateRef<unknown>","optional":true},{"name":"labelTooltip","type":"string","optional":true},{"name":"alwaysShowIcon","type":"boolean","optional":true},{"name":"context","type":"unknown","optional":true}]},{"name":"HtmlDescription","properties":[{"name":"text","type":"string","optional":true},{"name":"iconName","type":"IconName","optional":true},{"name":"iconTooltip","type":"string","optional":true}]}],"enums":[]},"examples":[],"documentation":"A step-by-step progress indicator for multi-stage workflows. Shows the user's current position, completed steps, and remaining steps at a glance. Ships two visual variants tied to specific product flows: the default `primary` belongs to the candidate test-taking experience, while `secondary` is the newer company-side style used in assessment/job creation wizards.\n\n## When to use\n\n- **`stepperVariant=\"secondary\"` for company-side wizards** — assessment/job creation flows (e.g. Job details → Choose tests → Add questions → Finalize) use the secondary variant\n- **`stepperVariant=\"primary\"` (the default) only in the candidate test-taking flow** — it is the test-taking-experience style, not the assessment-creation one\n- **Clear, brief step labels** — keep each step label to 2–3 words maximum\n- **Completed steps marked as completed** (not just 'past') so users can return and edit a previous step\n- **Flows with 3–6 steps** — fewer is often better handled with a simple header\n\n## When NOT to use\n\n- **The default `primary` variant in assessment creation or other company-side wizards** → that's the test-taking-flow style; company-side wizards use `stepperVariant=\"secondary\"`\n- **As a navigation menu** → it's for linear flows only, not arbitrary page-switching\n- **Jumping forward to uncompleted steps without validation** → enforce order where the workflow requires it\n- **Nesting one multi-step flow inside a step of another** → it destroys orientation\n\n## How to use\n\nTo use the Stepper component, you need to import the `StepperComponentModule` in your module:\n\n```typescript\nimport { StepperComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n StepperComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-stepper\n [steps]=\"[\n { label: 'Step 1', iconName: 'Team' },\n { label: 'Step 2', iconName: 'Test' },\n { label: 'Step 3', iconName: 'Delete' }\n ]\"\n position=\"horizontal\"\n [lastStepFinalIcon]=\"true\"\n applicationTheme=\"light\"\n (stepChange)=\"onStepChange($event)\"\n>\n</ui-stepper>\n```\n\nThe stepper component helps users navigate through a multi-step process and shows their progress.\n","whenToUse":["**`stepperVariant=\"secondary\"` for company-side wizards** — assessment/job creation flows (e.g. Job details → Choose tests → Add questions → Finalize) use the secondary variant","**`stepperVariant=\"primary\"` (the default) only in the candidate test-taking flow** — it is the test-taking-experience style, not the assessment-creation one","**Clear, brief step labels** — keep each step label to 2–3 words maximum","**Completed steps marked as completed** (not just 'past') so users can return and edit a previous step","**Flows with 3–6 steps** — fewer is often better handled with a simple header"],"whenNotToUse":["**The default `primary` variant in assessment creation or other company-side wizards** → that's the test-taking-flow style; company-side wizards use `stepperVariant=\"secondary\"`","**As a navigation menu** → it's for linear flows only, not arbitrary page-switching","**Jumping forward to uncompleted steps without validation** → enforce order where the workflow requires it","**Nesting one multi-step flow inside a step of another** → it destroys orientation"],"template":"<ng-container>\n <div style=\"position: absolute; left: -99999px\" [attr.aria-live]=\"'polite'\">\n <span>\n {{ itemSteps[selectedIndex]?.label ?? '' }}\n {{\n translationContext + 'SELECTED_OF'\n | uiTranslate: { selected: (selectedIndex ?? 0) + 1, total: itemSteps?.length ?? 0 }\n | async\n }}\n {{\n itemSteps[selectedIndex]?.isVisited &&\n itemSteps.length <= (selectedIndex ?? 0) + 1 &&\n itemSteps[selectedIndex + 1]?.isVisited\n ? (translationContext + 'COMPLETED' | uiTranslate | async)\n : (translationContext + 'INCOMPLETE' | uiTranslate | async)\n }}\n </span>\n </div>\n\n @if ((position === 'horizontal' && (isMobile$ | async) === false) || position === 'vertical') {\n <div\n class=\"stepper-wrapper\"\n role=\"tablist\"\n [ngClass]=\"[position, stepperVariant === 'secondary' ? 'secondary-variant' : '']\"\n [attr.theme]=\"applicationTheme\"\n >\n @for (step of itemSteps; track step; let i = $index; let isLast = $last) {\n <div class=\"step\" #stepElement>\n <ui-step\n [label]=\"step.label\"\n [isVisited]=\"step.isVisited!\"\n [icon]=\"step.iconName\"\n [index]=\"i\"\n [selected]=\"i === selectedIndex\"\n [selectedIndex]=\"selectedIndex\"\n [isLast]=\"isLast\"\n [disabled]=\"step.disabled ?? (i > selectedIndex + 1 && !step.isVisited)\"\n [prevStepIsVisited]=\"itemSteps[i - 1]?.isVisited && step.isVisited\"\n [finalIcon]=\"isLast ? lastStepFinalIcon : false\"\n [showIconWhenSelected]=\"!!step.showIconWhenSelected\"\n (stepSelected)=\"onSelect($event, step)\"\n [applicationTheme]=\"applicationTheme\"\n role=\"tab\"\n [attr.aria-controls]=\"'step-panel-' + i\"\n [attr.aria-selected]=\"i === selectedIndex\"\n [attr.aria-label]=\"step.label\"\n [attr.tabindex]=\"(step.disabled ?? (i > selectedIndex + 1 && !step.isVisited)) ? -1 : 0\"\n [htmlDescription]=\"step.htmlDescription ?? []\"\n [boldLabel]=\"!!step.boldLabel\"\n [position]=\"position\"\n [descriptionTpl]=\"step.descriptionTpl\"\n [labelTooltip]=\"step.labelTooltip\"\n [alwaysShowIcon]=\"!!step.alwaysShowIcon\"\n [context]=\"step.context\"\n [stepVariant]=\"stepperVariant\"\n [largeTabletMode]=\"isLargeTablet$ | async\"\n ></ui-step>\n </div>\n @if (!isLast && stepperVariant === 'primary') {\n <div class=\"separator-line\" [ngClass]=\"{ visited: step.isVisited && !!itemSteps[i + 1]?.isVisited }\">\n <div [stepLineElement]=\"stepElement\" [position]=\"position\" class=\"line\"></div>\n </div>\n }\n @if (!isLast && stepperVariant === 'secondary') {\n <div class=\"separator-arrow\">\n <ui-icon class=\"separator-icon\" name=\"Arrow-chevron-right-in-line\" size=\"24\" color=\"gray\"></ui-icon>\n </div>\n }\n }\n </div>\n } @else {\n <div class=\"mobile-stepper-wrapper\">\n <div\n class=\"stepper\"\n tabindex=\"0\"\n #stepper\n role=\"button\"\n [ngClass]=\"{ 'keyboard-focused': keyboardFocused() }\"\n (keydown.enter)=\"showStepsToggle()\"\n (keydown.space)=\"showStepsToggle()\"\n [attr.aria-expanded]=\"isOpen\"\n >\n <div class=\"progress-step\" [tabindex]=\"-1\" [attr.aria-hidden]=\"true\">\n <ui-radial-progress\n [applicationTheme]=\"applicationTheme\"\n [value]=\"selectedIndex + 1\"\n [total]=\"itemSteps.length\"\n ></ui-radial-progress>\n </div>\n <div class=\"step-info\">\n <div class=\"current-step\">\n <span>{{ selectedIndex + 1 }}/{{ itemSteps.length }} </span>\n <span>{{ itemSteps[selectedIndex].label }}</span>\n </div>\n <div class=\"next-step\">\n @if (itemSteps.length !== selectedIndex + 1) {\n <span>{{ ('COMMON.NEXT' | uiTranslate | async) + ':' }} {{ itemSteps[selectedIndex + 1].label }}</span>\n } @else {\n <span>{{ translationContext + 'FINAL_STEP' | uiTranslate | async }}</span>\n }\n </div>\n </div>\n <div\n class=\"open-steps\"\n tabindex=\"0\"\n role=\"button\"\n (keydown.enter)=\"showStepsToggle()\"\n (click)=\"showStepsToggle()\"\n [ngClass]=\"{ opened: isOpen }\"\n >\n <ui-icon\n data-testid=\"ui-stepper-icon\"\n [applicationTheme]=\"applicationTheme\"\n [size]=\"'24'\"\n [name]=\"applicationTheme === 'classic' ? 'Arrow_down' : 'Arrow-chevron-down-in-line'\"\n ></ui-icon>\n </div>\n </div>\n @if (isOpen) {\n <div>\n <div class=\"step-list\">\n @for (step of itemSteps; track step; let i = $index; let isLast = $last) {\n <div class=\"step\">\n <ui-step\n [label]=\"step.label\"\n [isVisited]=\"step.isVisited!\"\n [icon]=\"step.iconName\"\n [index]=\"i\"\n [selected]=\"i === selectedIndex\"\n [selectedIndex]=\"selectedIndex\"\n [isLast]=\"isLast\"\n [disabled]=\"step.disabled ?? (i > selectedIndex + 1 && !step.isVisited)\"\n [mobileMode]=\"true\"\n [finalIcon]=\"isLast ? lastStepFinalIcon : false\"\n [showIconWhenSelected]=\"!!step.showIconWhenSelected\"\n (stepSelected)=\"onSelect($event, step)\"\n [applicationTheme]=\"applicationTheme\"\n [attr.aria-label]=\"ariaLabel + '--' + step.label\"\n [stepVariant]=\"stepperVariant\"\n ></ui-step>\n </div>\n }\n </div>\n <div\n class=\"overlay\"\n tabindex=\"0\"\n role=\"button\"\n (keydown.enter)=\"onCloseStepList()\"\n (click)=\"onCloseStepList()\"\n ></div>\n </div>\n }\n </div>\n }\n</ng-container>\n","queryGuide":{"roles":["tablist","tab","button"],"ariaAttributes":["aria-live","aria-controls","aria-selected","aria-label","aria-expanded","aria-hidden"],"dataTestIds":["ui-stepper-icon"],"materialComponents":[],"childUIComponents":["ui-step","ui-icon","ui-radial-progress"],"suggestedQueries":["screen.getByRole('tablist')","screen.getByRole('tab')","screen.getByRole('button')","screen.getByLabelText('label text')","screen.getByTestId('ui-stepper-icon')"]},"filePath":"components/stepper/stepper.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=18332-40717","lastModified":1786626756419.7915},"table":{"name":"TableComponent","selector":"ui-table","category":"table","description":"The primary data-display component for rows of structured information. Supports sortable headers, checkboxes for bulk selection, badges, avatars, inline icon actions, segmented bars, and pagination. Used across assessment lists, candidate management, test library results, and team member management.","importPath":"@testgorilla/tgo-ui/components/table","moduleName":"TableComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"config","type":"TableConfigWithPagination<T>","defaultValue":null,"required":true,"alias":null,"transform":null,"description":"Table configuration","isSignal":false},{"name":"data","type":"TableData","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Input property for providing the table data.","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Input property indicating whether the table is in a loading state.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"tableDetailColumns","type":"TableDetailColumn<T, TDetail>","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Data structure to select which columns should be rendered and their capabilities","isSignal":false},{"name":"tableDetails","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"disableBorderRadius","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Disable border radius for table","isSignal":false},{"name":"isCondensed","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Condensed table","isSignal":false},{"name":"stickyHeader","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Sticky header","isSignal":false},{"name":"maxHeight","type":"unknown","defaultValue":"'576px'","required":false,"alias":null,"transform":null,"description":"Max height","isSignal":false}],"outputs":[{"name":"onRowClickEvent","eventType":"T & { index: number }","alias":null,"description":"","isSignal":false},{"name":"onDetailRowClickEvent","eventType":"TDetail","alias":null,"description":"","isSignal":false},{"name":"paginatorChange","eventType":"PageEvent","alias":null,"description":"","isSignal":false},{"name":"sortChange","eventType":"Sort","alias":null,"description":"","isSignal":false}],"module":{"name":"TableComponentModule","imports":["CommonModule","MatTableModule","MatTooltipModule","SkeletonComponent","PaginatorComponentModule","ButtonComponentModule","MatSortModule","IconComponentModule","EllipseTextDirective","DynamicComponentDirective","DataPropertyGetterPipe","MemoizeFuncPipe","TableComponent","SentenceCasePipe"],"exports":["TableComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"TableData","value":"Record<string, unknown>[]"},{"name":"SortDirection","value":"'asc' | 'desc' | ''"},{"name":"DataSource","value":"{\n [key: string]: unknown;\n}"},{"name":"ScrollSettings","value":"{ scrollEnd: boolean; horizontalScroll: boolean; scrollStart: boolean }"}],"interfaces":[{"name":"TableConfig","properties":[{"name":"columns","type":"TableColumn<T>[]","optional":false},{"name":"pagination","type":"Pagination","optional":true},{"name":"title","type":"string","optional":true},{"name":"width","type":"number","optional":true},{"name":"sort","type":"Sort","optional":true},{"name":"hidePagination","type":"boolean","optional":true},{"name":"noDataRowTpl","type":"TemplateRef<unknown>","optional":true},{"name":"sortable","type":"boolean","optional":true}]},{"name":"TableConfigWithPagination","properties":[{"name":"pagination","type":"Pagination","optional":true}]},{"name":"TableColumn","properties":[{"name":"title","type":"string","optional":true},{"name":"key","type":"string","optional":false},{"name":"allowMultiline","type":"boolean","optional":true},{"name":"bold","type":"boolean","optional":true},{"name":"showTooltip","type":"boolean","optional":true},{"name":"tooltip","type":"string","optional":true},{"name":"allowSort","type":"boolean","optional":true},{"name":"isSticky","type":"boolean","optional":true},{"name":"headerCellTemplate","type":"TemplateRef<unknown>","optional":true},{"name":"rowCellTemplate","type":"TemplateRef<unknown>","optional":true},{"name":"rowCellTemplateClick","type":"() => void","optional":true},{"name":"sortHeader","type":"string","optional":true},{"name":"hidden","type":"boolean","optional":true},{"name":"order","type":"number","optional":true},{"name":"fontSize","type":"'12px' | '14px'","optional":true},{"name":"field","type":"string","optional":true},{"name":"type","type":"ColumnType","optional":true},{"name":"function","type":"(v: T) => string","optional":true},{"name":"renderer","type":"(v: T) => ComponentRenderer | string","optional":true},{"name":"styles","type":"ColumnStyles","optional":true},{"name":"sortStart","type":"SortDirection","optional":true},{"name":"uniqueId","type":"string","optional":true}]},{"name":"Pagination","properties":[{"name":"itemsPerPageOptions","type":"number[]","optional":false},{"name":"itemsPerPage","type":"number","optional":false},{"name":"currentPage","type":"number","optional":false},{"name":"totalItems","type":"number","optional":false}]},{"name":"ColumnEllipse","properties":[{"name":"index","type":"number","optional":false},{"name":"showTooltip","type":"boolean","optional":false}]},{"name":"Sort","properties":[{"name":"active","type":"string","optional":false},{"name":"direction","type":"SortDirection","optional":false},{"name":"defaultSortStart","type":"SortDirection","optional":true}]},{"name":"TableDetailColumn","properties":[{"name":"columnDefs","type":"TableColumn<TDetail>[]","optional":false},{"name":"setDetailRowData","type":"(params: DetailRowDataParams<T, TDetail>) => void","optional":false}]},{"name":"DetailRowDataParams","properties":[{"name":"data","type":"T","optional":false}]},{"name":"ComponentRenderer","properties":[{"name":"component","type":"Type<unknown>","optional":false},{"name":"inputs","type":"Record<string, unknown>","optional":true},{"name":"outputs","type":"{ [key: string]: (event: unknown) => void }","optional":true}]},{"name":"ColumnStyles","properties":[{"name":"alignment","type":"ColumnAlignment","optional":true},{"name":"width","type":"string","optional":true},{"name":"'min-width'","type":"string","optional":true},{"name":"padding","type":"string","optional":true}]}],"enums":[{"name":"ColumnType","members":[{"name":"FIELD","value":"'field'"},{"name":"FUNCTION","value":"'function'"},{"name":"RENDERER","value":"'renderer'"}]},{"name":"ColumnAlignment","members":[{"name":"LEFT","value":"'left'"},{"name":"RIGHT","value":"'right'"},{"name":"CENTER","value":"'center'"}]},{"name":"ColumnTypeEnum","members":[{"name":"FIELD","value":"'field'"},{"name":"FUNCTION","value":"'function'"},{"name":"RENDERER","value":"'renderer'"}]},{"name":"ColumnAlignmentEnum","members":[{"name":"LEFT","value":"'left'"},{"name":"RIGHT","value":"'right'"},{"name":"CENTER","value":"'center'"}]}]},"examples":[],"documentation":"The primary data-display component for rows of structured information. Supports sortable headers, checkboxes for bulk selection, badges, avatars, inline icon actions, segmented bars, and pagination. Used across assessment lists, candidate management, test library results, and team member management.\n\n## When to use\n\n- **Sortable column headers** when the order of results is meaningful (date, score, name) and users have a reason to re-order\n- **Bulk Actions bar** only when at least one row is selected — don't show it empty\n- **The correct cell variant for its data type** — badge cells for status, avatar cells for people, icon-button cells for row actions\n\n## When NOT to use\n\n- **More than 3 icon action buttons in a single row** → use an Overflow Menu for additional actions\n- **5 or fewer rows that don't need sorting or filtering** → a simple Card list reads better\n- **Hierarchical data** → Table is for flat, comparable datasets only\n","whenToUse":["**Sortable column headers** when the order of results is meaningful (date, score, name) and users have a reason to re-order","**Bulk Actions bar** only when at least one row is selected — don't show it empty","**The correct cell variant for its data type** — badge cells for status, avatar cells for people, icon-button cells for row actions"],"whenNotToUse":["**More than 3 icon action buttons in a single row** → use an Overflow Menu for additional actions","**5 or fewer rows that don't need sorting or filtering** → a simple Card list reads better","**Hierarchical data** → Table is for flat, comparable datasets only"],"template":"<div\n #tableContainer\n class=\"table-container\"\n [ngClass]=\"{ 'without-title': !tableConfig?.title, 'disable-border-radius': disableBorderRadius }\"\n [attr.theme]=\"applicationTheme\"\n>\n @if (tableConfig?.title) {\n <div class=\"table-header\">\n {{ tableConfig.title | sentencecase }}\n <ng-content select=\"[title-actions]\"></ng-content>\n </div>\n }\n @if (scrollSettings$ | async; as scrollSettings) {\n <div #table class=\"table-wrapper\" [class.sticky-header]=\"stickyHeader\" [style.--table-max-height]=\"maxHeight\">\n <table\n data-testid=\"table-main\"\n [tabIndex]=\"0\"\n class=\"main-table\"\n [class.condensed]=\"isCondensed\"\n [class.sort-disabled]=\"loading\"\n mat-table\n [dataSource]=\"tableData\"\n matSort\n [matSortStart]=\"tableConfig?.sort?.defaultSortStart ?? 'asc'\"\n [matSortActive]=\"tableConfig?.sort?.active ?? ''\"\n [matSortDirection]=\"tableConfig?.sort?.direction ?? ''\"\n (matSortChange)=\"onSortChange($event)\"\n multiTemplateDataRows\n >\n @for (column of tableConfig.columns; track trackColumnUniqueId(i, column); let i = $index; let isLast = $last) {\n @if (isLast && column.isSticky && column.allowSort) {\n <ng-container [matColumnDef]=\"column.uniqueId ?? column.key\" [stickyEnd]=\"!scrollSettings?.scrollEnd\">\n <th\n mat-header-cell\n [start]=\"column.sortStart ?? 'asc'\"\n [mat-sort-header]=\"column.sortHeader ?? column.key\"\n *matHeaderCellDef\n [attr.sortable]=\"column.allowSort ?? false\"\n [ngStyle]=\"{ width: column.styles?.width, minWidth: column.styles?.['min-width'] }\"\n [ngClass]=\"column.styles?.alignment ? 'th-' + column.styles?.alignment : ''\"\n [style.font-weight]=\"'bold'\"\n >\n @if (column.headerCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n column.headerCellTemplate;\n context: { $implicit: column.title, column, colIndex: i }\n \"\n ></ng-container>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"columnHeader\"\n [ngTemplateOutletContext]=\"{ i, column }\"\n ></ng-container>\n }\n </th>\n <td\n mat-cell\n *matCellDef=\"let element; let rowIndex = dataIndex\"\n [align]=\"column.styles?.alignment\"\n [ngClass]=\"[column.styles?.alignment ? 'td-' + column.styles?.alignment : '']\"\n (click)=\"\n onRowClick(element, rowIndex); column?.rowCellTemplateClick ? column!.rowCellTemplateClick?.() : ''\n \"\n >\n @if (loading) {\n <ui-skeleton\n class=\"table-skeleton-loader\"\n [count]=\"1\"\n [theme]=\"skeletonStyleConfig\"\n [appearance]=\"'line'\"\n ></ui-skeleton>\n } @else {\n <div class=\"row-value\">\n <ng-container\n [ngTemplateOutlet]=\"expandButton\"\n [ngTemplateOutletContext]=\"{ index: i, element }\"\n ></ng-container>\n @if (column?.type) {\n <ng-container\n *ngTemplateOutlet=\"tableDataTpl; context: { element, tableColumn: column }\"\n ></ng-container>\n } @else {\n @if (column.rowCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.rowCellTemplate; context: { $implicit: element, rowIndex }\"\n ></ng-container>\n } @else {\n <span\n [style.font-weight]=\"column.bold ? 'bold' : 'normal'\"\n [style.font-size]=\"column.fontSize ?? '14px'\"\n >{{ element | dataPropertyGetter: column.key }}</span\n >\n }\n }\n </div>\n }\n </td>\n </ng-container>\n }\n @if (isLast && column.isSticky && !column.allowSort) {\n <ng-container\n [matColumnDef]=\"column.uniqueId ?? column.key\"\n [stickyEnd]=\"scrollSettings.horizontalScroll ? !scrollSettings.scrollEnd : true\"\n >\n <th\n mat-header-cell\n *matHeaderCellDef\n [attr.sortable]=\"column.allowSort ?? false\"\n [ngStyle]=\"{ width: column.styles?.width, minWidth: column.styles?.['min-width'] }\"\n [ngClass]=\"column.styles?.alignment ? 'th-' + column.styles?.alignment : ''\"\n [style.font-weight]=\"'bold'\"\n >\n @if (column.headerCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n column.headerCellTemplate;\n context: { $implicit: column.title, column, colIndex: i }\n \"\n ></ng-container>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"columnHeader\"\n [ngTemplateOutletContext]=\"{ i, column }\"\n ></ng-container>\n }\n </th>\n <td\n mat-cell\n *matCellDef=\"let element; let rowIndex = dataIndex\"\n [align]=\"column.styles?.alignment\"\n [ngClass]=\"[column.styles?.alignment ? 'td-' + column.styles?.alignment : '']\"\n (click)=\"\n onRowClick(element, rowIndex); column?.rowCellTemplateClick ? column!.rowCellTemplateClick?.() : ''\n \"\n >\n @if (loading) {\n <ui-skeleton\n class=\"table-skeleton-loader\"\n [count]=\"1\"\n [theme]=\"skeletonStyleConfig\"\n [appearance]=\"'line'\"\n ></ui-skeleton>\n } @else {\n <div class=\"row-value\">\n <ng-container\n [ngTemplateOutlet]=\"expandButton\"\n [ngTemplateOutletContext]=\"{ index: i, element }\"\n ></ng-container>\n @if (column?.type) {\n <ng-container\n *ngTemplateOutlet=\"tableDataTpl; context: { element, tableColumn: column }\"\n ></ng-container>\n } @else {\n @if (column.rowCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.rowCellTemplate; context: { $implicit: element, rowIndex }\"\n ></ng-container>\n } @else {\n <span\n [style.font-weight]=\"column.bold ? 'bold' : 'normal'\"\n [style.font-size]=\"column.fontSize ?? '14px'\"\n >{{ element | dataPropertyGetter: column.key }}</span\n >\n }\n }\n </div>\n }\n </td>\n </ng-container>\n }\n @if (!(isLast && column.isSticky) && column.allowSort) {\n <ng-container\n [matColumnDef]=\"column.uniqueId ?? column.key\"\n [sticky]=\"(isMobile$ | async) ? false : !scrollSettings.scrollStart && !!column.isSticky\"\n >\n <th\n mat-header-cell\n [start]=\"column.sortStart ?? 'asc'\"\n [mat-sort-header]=\"column.sortHeader ?? column.key\"\n [attr.sortable]=\"column.allowSort ?? false\"\n *matHeaderCellDef\n [style.font-weight]=\"'bold'\"\n [ngStyle]=\"{ width: column.styles?.width, minWidth: column.styles?.['min-width'] }\"\n [ngClass]=\"column.styles?.alignment ? 'th-' + column.styles?.alignment : ''\"\n >\n @if (column.headerCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n column.headerCellTemplate;\n context: { $implicit: column.title, column, colIndex: i }\n \"\n ></ng-container>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"columnHeader\"\n [ngTemplateOutletContext]=\"{ i, column }\"\n ></ng-container>\n }\n </th>\n <td\n mat-cell\n *matCellDef=\"let element; let rowIndex = dataIndex\"\n [align]=\"column.styles?.alignment\"\n [ngClass]=\"[column.styles?.alignment ? 'td-' + column.styles?.alignment : '']\"\n (click)=\"\n onRowClick(element, rowIndex); column?.rowCellTemplateClick ? column!.rowCellTemplateClick?.() : ''\n \"\n >\n @if (loading) {\n <ui-skeleton\n class=\"table-skeleton-loader\"\n [count]=\"1\"\n [theme]=\"skeletonStyleConfig\"\n [appearance]=\"'line'\"\n ></ui-skeleton>\n } @else {\n <div class=\"row-value\">\n <ng-container\n [ngTemplateOutlet]=\"expandButton\"\n [ngTemplateOutletContext]=\"{ index: i, element }\"\n ></ng-container>\n @if (column?.type) {\n <ng-container\n *ngTemplateOutlet=\"tableDataTpl; context: { element, tableColumn: column }\"\n ></ng-container>\n } @else {\n @if (column.rowCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.rowCellTemplate; context: { $implicit: element, rowIndex }\"\n ></ng-container>\n } @else {\n <span\n [style.font-weight]=\"column.bold ? 'bold' : 'normal'\"\n [style.font-size]=\"column.fontSize ?? '14px'\"\n >{{ element | dataPropertyGetter: column.key }}</span\n >\n }\n }\n </div>\n }\n </td>\n </ng-container>\n }\n @if (!(isLast && column.isSticky) && !column.allowSort) {\n <ng-container\n [matColumnDef]=\"column.uniqueId ?? column.key\"\n [sticky]=\"(isMobile$ | async) ? false : !scrollSettings.scrollStart && !!column.isSticky\"\n >\n <th\n mat-header-cell\n [attr.sortable]=\"column.allowSort ?? false\"\n *matHeaderCellDef\n [style.font-weight]=\"'bold'\"\n [ngStyle]=\"{ width: column.styles?.width, minWidth: column.styles?.['min-width'] }\"\n [ngClass]=\"column.styles?.alignment ? 'th-' + column.styles?.alignment : ''\"\n >\n @if (column.headerCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n column.headerCellTemplate;\n context: { $implicit: column.title, column, colIndex: i }\n \"\n ></ng-container>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"columnHeader\"\n [ngTemplateOutletContext]=\"{ i, column }\"\n ></ng-container>\n }\n </th>\n <td\n mat-cell\n *matCellDef=\"let element; let rowIndex = dataIndex\"\n [align]=\"column.styles?.alignment\"\n [ngClass]=\"[column.styles?.alignment ? 'td-' + column.styles?.alignment : '']\"\n (click)=\"\n onRowClick(element, rowIndex); column?.rowCellTemplateClick ? column!.rowCellTemplateClick?.() : ''\n \"\n >\n @if (loading) {\n <ui-skeleton\n class=\"table-skeleton-loader\"\n [count]=\"1\"\n [theme]=\"skeletonStyleConfig\"\n [appearance]=\"'line'\"\n ></ui-skeleton>\n } @else {\n <div class=\"row-value\">\n <ng-container\n [ngTemplateOutlet]=\"expandButton\"\n [ngTemplateOutletContext]=\"{ index: i, element }\"\n ></ng-container>\n @if (column?.type) {\n <ng-container\n *ngTemplateOutlet=\"tableDataTpl; context: { element, tableColumn: column }\"\n ></ng-container>\n } @else {\n @if (column.rowCellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"column.rowCellTemplate; context: { $implicit: element, rowIndex }\"\n ></ng-container>\n } @else {\n <span\n [style.font-weight]=\"column.bold ? 'bold' : 'normal'\"\n [style.font-size]=\"column.fontSize ?? '14px'\"\n >{{ element | dataPropertyGetter: column.key }}</span\n >\n }\n }\n </div>\n }\n </td>\n </ng-container>\n }\n }\n <ng-container matColumnDef=\"expandedDetail\">\n <td mat-cell *matCellDef=\"let element\" [attr.colspan]=\"displayedColumns.length\">\n @if (dataSourceDetail.data.length) {\n <div class=\"element-detail\" [@expandDetail]=\"element === elementDetail ? 'expanded' : 'collapsed'\">\n <table\n data-testid=\"table-detail\"\n mat-table\n [dataSource]=\"dataSourceDetail\"\n [ngClass]=\"{ 'hide-borders': element === tableData[tableData.length - 1] }\"\n >\n @for (tableColumn of tableDetailColumns.columnDefs; track tableColumn) {\n <ng-container [matColumnDef]=\"tableColumn.title ?? ''\">\n <th\n mat-header-cell\n *matHeaderCellDef=\"let element\"\n arrowPosition=\"after\"\n [ngStyle]=\"{ width: tableColumn.styles?.width }\"\n [ngClass]=\"tableColumn.styles?.alignment ? 'th-' + tableColumn.styles?.alignment : ''\"\n >\n {{ tableColumn.title | uppercase }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n (click)=\"onDetailRowClick(element)\"\n [align]=\"tableColumn.styles?.alignment\"\n [ngClass]=\"tableColumn.styles?.alignment ? 'td-' + tableColumn.styles?.alignment : ''\"\n [ngStyle]=\"{ width: tableColumn.styles?.width }\"\n >\n <ng-container\n *ngTemplateOutlet=\"tableDataTpl; context: { element, tableColumn }\"\n ></ng-container>\n </td>\n </ng-container>\n }\n <tr mat-header-row *matHeaderRowDef=\"columnsDetailToDisplay\"></tr>\n <tr mat-row *matRowDef=\"let rowDetail; columns: columnsDetailToDisplay\"></tr>\n </table>\n </div>\n }\n </td>\n </ng-container>\n <ng-template #tableDataTpl let-element=\"element\" let-tableColumn=\"tableColumn\">\n <div class=\"align-wrapper\">\n @switch (tableColumn.type) {\n @case (DataType.FIELD) {\n {{ element | dataPropertyGetter: tableColumn.key }}\n }\n @case (DataType.FUNCTION) {\n {{ tableColumn.function?.(element) }}\n }\n @case (DataType.RENDERER) {\n <ng-template [dynamicComponent]=\"$any([tableColumn.renderer, element] | memoizeFunc: callRenderer)\">\n </ng-template>\n }\n @default {\n {{ element | dataPropertyGetter: tableColumn.key }}\n }\n }\n </div>\n </ng-template>\n <tr\n mat-header-row\n *matHeaderRowDef=\"displayedColumns; sticky: stickyHeader\"\n [class.is-title-present]=\"tableConfig.title\"\n ></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: displayedColumns\"\n [ngClass]=\"{\n 'expanded-row': elementDetail === row,\n 'row-clickable': tableDetails,\n selected: row?.selected,\n 'last-row': row === tableData[tableData.length - 1] && !elementDetail,\n }\"\n (click)=\"toggleRow(row)\"\n ></tr>\n @if (tableDetails && tableDetailColumns?.columnDefs?.length) {\n <tr mat-row *matRowDef=\"let row; columns: ['expandedDetail']\" class=\"detail-row\"></tr>\n }\n @if (tableConfig.noDataRowTpl) {\n <tr *matNoDataRow>\n <ng-container *ngTemplateOutlet=\"tableConfig.noDataRowTpl\"></ng-container>\n </tr>\n }\n </table>\n </div>\n }\n\n @if (!tableConfig?.hidePagination && tableConfig?.pagination) {\n <ui-paginator\n [disabled]=\"loading\"\n [applicationTheme]=\"applicationTheme !== 'classic' ? 'light' : 'classic'\"\n [pageSize]=\"tableConfig.pagination?.itemsPerPage ?? 25\"\n [length]=\"tableConfig.pagination?.totalItems ?? 0\"\n [currentPage]=\"tableConfig.pagination?.currentPage ?? 0\"\n [pageSizeOptions]=\"tableConfig.pagination?.itemsPerPageOptions ?? [10, 25, 50]\"\n (paginatorChange)=\"onPaginatorChange($event)\"\n ></ui-paginator>\n }\n</div>\n\n<ng-template #expandButton let-i=\"index\" let-element=\"element\">\n @if (!i && tableDetails) {\n <ui-button\n [applicationTheme]=\"applicationTheme\"\n variant=\"icon-button\"\n [ngClass]=\"{ opened: elementDetail && element === elementDetail }\"\n [iconName]=\"\n elementDetail && element === elementDetail ? 'Arrow-chevron-up-in-line' : 'Arrow-chevron-down-in-line'\n \"\n ></ui-button>\n }\n</ng-template>\n\n<ng-template #columnHeader let-column=\"column\" let-i=\"i\">\n <div class=\"header-section\">\n <span uiEllipseText (onChangeTextState)=\"onChangeColumnSize(i, $event)\" class=\"column-title\"\n >{{ column.title | sentencecase }}\n </span>\n @if (column?.showTooltip) {\n <ui-icon\n [name]=\"'Help-in-line'\"\n [matTooltipClass]=\"applicationTheme\"\n [matTooltip]=\"(column?.showTooltip ? (column?.tooltip ? column.tooltip : column.title) : '') || ''\"\n ></ui-icon>\n }\n </div>\n</ng-template>\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":["table-main","table-detail"],"materialComponents":[],"childUIComponents":["ui-skeleton","ui-paginator","ui-button","ui-icon"],"suggestedQueries":["screen.getByTestId('table-main')","screen.getByTestId('table-detail')"]},"filePath":"components/table/table.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2409-41148","lastModified":1786626756420.3782},"tabs":{"name":"TabsComponent","selector":"ui-tabs","category":"tabs","description":"A tabbed navigation control that switches between related content sections within the same page or component. Available in three sizes (64px, 56px, 32px). Used to switch between Results, Settings, and Invitations within an assessment; between Active, Archived, and Drafts in the jobs list; or between different score dimensions in candidate results.","importPath":"@testgorilla/tgo-ui/components/tabs","moduleName":"TabsComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"companyColor","type":"string | null","defaultValue":"'#46A997'","required":false,"alias":null,"transform":null,"description":"Color of the Tabs.\nDefaults to Test Gorilla primary color.","isSignal":false},{"name":"type","type":"TabsType","defaultValue":"'underlined'","required":false,"alias":null,"transform":null,"description":"Type the Tabs.\nDefaults underlined.","isSignal":false},{"name":"animationDuration","type":"number","defaultValue":"300","required":false,"alias":null,"transform":null,"description":"Animation duration when switching tabs","isSignal":false},{"name":"headerContentPadding","type":"unknown","defaultValue":"'32px'","required":false,"alias":null,"transform":"(value: number): string => `${value}px`","description":"Sets the padding for content and header","isSignal":false},{"name":"dynamicHeight","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":"booleanAttribute","description":"Sets the dynamic height of the tab","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"tabHeaderSize","type":"TabHeaderSize","defaultValue":"TAB_HEADER_SIZE.BIG","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"isLoading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"selectedTabName","type":"string | null","defaultValue":"null","required":false,"alias":null,"transform":null,"description":"Name of the tab to show. Applied as tabs register, so a tab requested before the\nprojected `ng-template uiTab` children exist — a deep link, a query param — is still\nhonoured on first paint, with no need to call `selectTab()` from a lifecycle hook.\n\nThis is a request, not live state: it is applied when the value changes and once more as\nsoon as the named tab registers, but a later click or a change to the tab list will not\nsnap the selection back to it. An unknown name is a no-op — while tabs are still\nregistering every name is unknown, so it cannot warn or throw. Bind a name the consumer\nhas already validated.\n\nApplying it does not emit `selectedTab` / `selectedTabIndex` (the consumer supplied the\nvalue, so echoing it back would double-handle it) and does not consult the current tab's\n`canLeave` — declarative selection is the consumer's own decision, so it owns any\nguarding around the value it binds. User-driven switches still honour `canLeave`.","isSignal":true}],"outputs":[{"name":"selectedTabIndex","eventType":"number","alias":null,"description":"","isSignal":false},{"name":"selectedTab","eventType":"Tab","alias":null,"description":"","isSignal":false}],"module":{"name":"TabsComponentModule","imports":["TabDirective","CommonModule","MatTabsModule","IconComponentModule","UiTranslatePipe","SkeletonComponent","TabsComponent"],"exports":["TabsComponent","TabDirective"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"TabsType","value":"'underlined' | 'filled'"},{"name":"TabHeaderSize","value":"(typeof TAB_HEADER_SIZE)[keyof typeof TAB_HEADER_SIZE]"}],"interfaces":[{"name":"Tab","properties":[{"name":"tabName","type":"string","optional":false},{"name":"tabLabel","type":"string","optional":false},{"name":"contentTemplateRef","type":"TemplateRef<unknown>","optional":false},{"name":"iconLeft","type":"IconName","optional":true},{"name":"iconRight","type":"IconName","optional":true},{"name":"disabled","type":"boolean","optional":true},{"name":"canLeave","type":"(ctx?: { currentTab: Tab; nextTab: Tab }) => boolean | Promise<boolean>","optional":true},{"name":"linkUrl","type":"string","optional":true},{"name":"ariaLabel","type":"string","optional":true},{"name":"order","type":"number","optional":true},{"name":"skeletonTheme","type":"NgxSkeletonLoaderConfigTheme","optional":true},{"name":"isSkeletonAiTheme","type":"boolean","optional":true},{"name":"isLabelLoading","type":"boolean","optional":true}]}],"enums":[]},"examples":[{"name":"Default","template":"<ui-tabs type=\"underlined\">\n <ng-template uiTab [tabLabel]=\"'Tab 1'\" [tabName]=\"'1'\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut mauris sit amet ligula tristique interdum eu non sapien. Integer nec turpis vel augue volutpat convallis. Nullam bibendum elit ac ex convallis, a ultricies lacus consequat. Fusce sit amet dapibus ipsum. Proin vel nunc nec justo lacinia rhoncus. Nunc euismod vestibulum tellus, vel vestibulum diam convallis quis. Integer in vehicula ipsum. Duis pulvinar quam a elit interdum, at euismod justo condimentum. Vivamus eu semper dolor. Sed sit amet tristique odio. Mauris ut felis vel mi pharetra cursus. Aenean ullamcorper fringilla lectus, vel facilisis ligula blandit eget.\n Curabitur eu nisl ac orci condimentum convallis. Sed auctor mi non ex hendrerit, sit amet varius justo malesuada. Aenean euismod felis a dui facilisis, id hendrerit nulla efficitur. Etiam varius massa\n </ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 2'\" [tabName]=\"'2'\" >Quisque in justo vel justo commodo fermentum. Morbi eget tincidunt libero. Integer ut velit in felis vehicula ultrices. Nulla facilisi. Praesent vehicula elit id semper accumsan. Phasellus sed tortor sed nulla fermentum cursus in id enim. Ut et aliquam velit. In hac habitasse platea dictumst. Nulla facilisi. Sed sagittis dolor in mi ultricies facilisis. Ut sit amet turpis vel tortor congue posuere. Aenean luctus quam sit amet augue congue, nec fermentum dolor volutpat.</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 3'\" [tabName]=\"'3'\" [iconLeft]=\"'Level'\" [iconRight]=\"'Test'\">Curabitur eu nisl ac orci condimentum convallis. Sed auctor mi non ex hendrerit, sit amet varius justo malesuada. Aenean euismod felis a dui facilisis, id hendrerit nulla efficitur. Etiam varius massa a est eleifend, non scelerisque mauris dictum. Proin vel metus a justo efficitur aliquet nec ut tortor. Integer pulvinar imperdiet velit non aliquam. Fusce nec elit vitae orci facilisis tristique eu vitae dolor. Integer tristique ex at dui facilisis aliquam. Maecenas interdum tortor id aliquam facilisis. Suspendisse tristique vestibulum nulla, id aliquet tortor convallis vel. Vivamus ullamcorper, lectus et feugiat posuere, tellus dolor vulputate tortor, vel lacinia leo libero et nunc.</ng-template>\n </ui-tabs>","props":{}},{"name":"Filled","template":"<ui-tabs type=\"filled\">\n <ng-template uiTab [tabLabel]=\"'Tab 1'\" [tabName]=\"'1'\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut mauris sit amet ligula tristique interdum eu non sapien. Integer nec turpis vel augue volutpat convallis. Nullam bibendum elit ac ex convallis, a ultricies lacus consequat. Fusce sit amet dapibus ipsum. Proin vel nunc nec justo lacinia rhoncus. Nunc euismod vestibulum tellus, vel vestibulum diam convallis quis. Integer in vehicula ipsum. Duis pulvinar quam a elit interdum, at euismod justo condimentum. Vivamus eu semper dolor. Sed sit amet tristique odio. Mauris ut felis vel mi pharetra cursus. Aenean ullamcorper fringilla lectus, vel facilisis ligula blandit eget.\n Curabitur eu nisl ac orci condimentum convallis. Sed auctor mi non ex hendrerit, sit amet varius justo malesuada. Aenean euismod felis a dui facilisis, id hendrerit nulla efficitur. Etiam varius massa\n </ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 2'\" [tabName]=\"'2'\">Quisque in justo vel justo commodo fermentum. Morbi eget tincidunt libero. Integer ut velit in felis vehicula ultrices. Nulla facilisi. Praesent vehicula elit id semper accumsan. Phasellus sed tortor sed nulla fermentum cursus in id enim. Ut et aliquam velit. In hac habitasse platea dictumst. Nulla facilisi. Sed sagittis dolor in mi ultricies facilisis. Ut sit amet turpis vel tortor congue posuere. Aenean luctus quam sit amet augue congue, nec fermentum dolor volutpat.</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 3'\" [tabName]=\"'3'\" [iconLeft]=\"'Level'\" [iconRight]=\"'Test'\">Curabitur eu nisl ac orci condimentum convallis. Sed auctor mi non ex hendrerit, sit amet varius justo malesuada. Aenean euismod felis a dui facilisis, id hendrerit nulla efficitur. Etiam varius massa a est eleifend, non scelerisque mauris dictum. Proin vel metus a justo efficitur aliquet nec ut tortor. Integer pulvinar imperdiet velit non aliquam. Fusce nec elit vitae orci facilisis tristique eu vitae dolor. Integer tristique ex at dui facilisis aliquam. Maecenas interdum tortor id aliquam facilisis. Suspendisse tristique vestibulum nulla, id aliquet tortor convallis vel. Vivamus ullamcorper, lectus et feugiat posuere, tellus dolor vulputate tortor, vel lacinia leo libero et nunc.</ng-template>\n </ui-tabs>","props":{}},{"name":"SmallHeaderSize","template":"<ui-tabs type=\"underlined\" tabHeaderSize=\"small\">\n <ng-template uiTab [tabLabel]=\"'Tab 1'\" [tabName]=\"'1'\">Tab 1 content</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 2'\" [tabName]=\"'2'\">Tab 2 content</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 3'\" [tabName]=\"'3'\">Tab 3 content</ng-template>\n </ui-tabs>","props":{}},{"name":"MediumHeaderSize","template":"<ui-tabs type=\"underlined\" tabHeaderSize=\"medium\">\n <ng-template uiTab [tabLabel]=\"'Tab 1'\" [tabName]=\"'1'\">Tab 1 content</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 2'\" [tabName]=\"'2'\">Tab 2 content</ng-template>\n <ng-template uiTab [tabLabel]=\"'Tab 3'\" [tabName]=\"'3'\">Tab 3 content</ng-template>\n </ui-tabs>","props":{}},{"name":"LoadingLabel","template":"<div style=\"display: flex; flex-direction: column; gap: 20px;\">\n <div>\n <button\n (click)=\"toggle()\"\n style=\"padding: 8px 16px; background-color: #D410AA; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\"\n >\n {{ counts.tab2 === null ? 'Finish' : 'Restart' }} loading the middle count\n </button>\n </div>\n\n <ui-tabs type=\"underlined\">\n @for (name of ['tab1', 'tab2', 'tab3']; track name; let i = $index) {\n <ng-template\n uiTab\n [tabLabel]=\"'Tab ' + (i + 1) + (counts[name] === null ? '' : ' (' + counts[name] + ')')\"\n [tabName]=\"name\"\n [order]=\"i + 1\"\n [isLabelLoading]=\"counts[name] === null\"\n >\n <div style=\"padding: 20px; border: 1px solid #eee; border-radius: 4px; margin-top: 20px;\">\n <h3>{{ name }}</h3>\n <p>Count: {{ counts[name] === null ? 'loading…' : counts[name] }}</p>\n </div>\n </ng-template>\n }\n </ui-tabs>\n </div>","props":{}},{"name":"DeepLinkedTab","template":"<div style=\"display: flex; flex-direction: column; gap: 20px;\">\n <div style=\"font-size: 14px;\">\n <button\n (click)=\"mounted ? unmount() : mount()\"\n style=\"padding: 8px 16px; background-color: #D410AA; color: white; border: none; border-radius: 4px; cursor: pointer;\"\n >\n {{ mounted ? 'Unmount' : 'Render the tab group' }}\n </button>\n <label style=\"margin-left: 12px;\">\n requested tab:\n <select [(ngModel)]=\"requestedTab\">\n <option value=\"tab1\">tab1</option>\n <option value=\"tab2\">tab2</option>\n <option value=\"tab3\">tab3</option>\n </select>\n </label>\n </div>\n\n <p style=\"font-size: 13px; color: #666; margin: 0;\">\n Unmount and re-mount to repeat the first-paint check with a different requested tab — no page\n reload needed.\n </p>\n\n @if (mounted) {\n <ui-tabs type=\"underlined\" [selectedTabName]=\"requestedTab\">\n @for (name of ['tab1', 'tab2', 'tab3']; track name; let i = $index) {\n <ng-template uiTab [tabLabel]=\"'Tab ' + (i + 1)\" [tabName]=\"name\" [order]=\"i + 1\">\n <div style=\"padding: 20px; border: 1px solid #eee; border-radius: 4px; margin-top: 20px;\">\n <h3>{{ name }}</h3>\n <input\n placeholder=\"type here — state survives a tab switch\"\n style=\"padding: 6px; width: 280px;\"\n />\n </div>\n </ng-template>\n }\n </ui-tabs>\n } @else {\n <p style=\"font-size: 14px; color: #666;\">\n The group does not exist yet — as far as a consumer is concerned, this is the window in which\n an imperative <code>selectTab()</code> call would be silently dropped.\n </p>\n }\n </div>","props":{}},{"name":"ConditionalTabs","template":"<div style=\"display: flex; flex-direction: column; gap: 20px;\">\n <div style=\"margin-bottom: 20px;\">\n <button\n (click)=\"toggleMiddleTab()\"\n style=\"padding: 8px 16px; background-color: #D410AA; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\"\n >\n {{ showMiddleTab ? 'Hide' : 'Show' }} Middle Tab\n </button>\n <span style=\"margin-left: 10px; font-size: 14px;\">\n Middle tab is currently: <strong>{{ showMiddleTab ? 'VISIBLE' : 'HIDDEN' }}</strong>\n </span>\n </div>\n\n <div style=\"margin-bottom: 20px; font-size: 14px;\">\n <span>Request a tab by name:</span>\n @for (name of ['tab1', 'tab2', 'tab3']; track name) {\n <button\n (click)=\"requestTab(name)\"\n style=\"margin-left: 8px; padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer;\"\n >\n {{ name }}\n </button>\n }\n <span style=\"margin-left: 10px;\">\n <code>[selectedTabName]</code> = <strong>{{ requestedTab ?? 'null' }}</strong>\n </span>\n </div>\n\n <div style=\"border: 1px solid #ddd; padding: 10px; background-color: #fafafa; border-radius: 4px; margin-bottom: 20px;\">\n <p><strong>Instructions:</strong></p>\n <p>In this example, we're using the <code>[order]</code> property to explicitly set the position of each tab:</p>\n <ul>\n <li>Tab 1: <code>[order]=\"1\"</code></li>\n <li>Tab 2: <code>[order]=\"2\"</code> (conditional tab with *ngIf or if)</li>\n <li>Tab 3: <code>[order]=\"3\"</code></li>\n </ul>\n <p>This ensures tabs always appear in the correct order, regardless of conditional rendering.</p>\n </div>\n\n <ui-tabs type=\"underlined\" [selectedTabName]=\"requestedTab\">\n <ng-template uiTab [tabLabel]=\"'Tab 1'\" [tabName]=\"'tab1'\" [order]=\"1\">\n <div style=\"padding: 20px; border: 1px solid #eee; border-radius: 4px; margin-top: 20px;\">\n <h3>First Tab Content</h3>\n <p>This tab is always visible with order=\"1\".</p>\n </div>\n </ng-template>\n @if (showMiddleTab) {\n <ng-template uiTab [tabLabel]=\"'Tab 2'\" [tabName]=\"'tab2'\" [order]=\"2\">\n <div style=\"padding: 20px; border: 1px solid #eee; border-radius: 4px; margin-top: 20px;\">\n <h3>Middle Tab Content</h3>\n <p>This tab is conditionally visible with order=\"2\".</p>\n <p>Try toggling this tab using the button above and observe that when it's shown again,\n it maintains its position between Tab 1 and Tab 3.</p>\n </div>\n </ng-template>\n }\n\n <ng-template uiTab [tabLabel]=\"'Tab 3'\" [tabName]=\"'tab3'\" [order]=\"3\">\n <div style=\"padding: 20px; border: 1px solid #eee; border-radius: 4px; margin-top: 20px;\">\n <h3>Last Tab Content</h3>\n <p>This tab is always visible with order=\"3\".</p>\n <p>When the middle tab is hidden, this tab should still remain in the last position and NOT move to the middle.</p>\n </div>\n </ng-template>\n </ui-tabs>\n </div>","props":{"type":"'underlined'"}}],"documentation":"A tabbed navigation control that switches between related content sections within the same page or component. Available in three sizes (64px, 56px, 32px). Used to switch between Results, Settings, and Invitations within an assessment; between Active, Archived, and Drafts in the jobs list; or between different score dimensions in candidate results.\n\n## When to use\n\n- **Parallel, repeatable switching** — use Tabs when the sections are siblings, not steps, and users may want to move between them repeatedly\n- **Tight vertical space** — use the 32px small variant inside Cards or panels where space is limited\n- **Sensible default** — always pre-select the most relevant tab for the context\n\n## When NOT to use\n\n- **Sequential flows that must be completed in order** → use Stepper instead\n- **More than 5–6 tabs** → a side navigation or filter approach scales better beyond that\n- **Content the user will always need** → if they need all sections, show them all rather than hiding them behind Tabs\n\n## How to use\n\nImport the standalone `TabsComponent` and `TabDirective`:\n\n```typescript\nimport { TabDirective, TabsComponent } from '@testgorilla/tgo-ui/components/tabs';\n\n@Component({\n imports: [TabsComponent, TabDirective],\n // ...\n})\nexport class YourComponent {}\n```\n\nThen use the component in your templates:\n\n```html\n<ui-tabs type=\"underlined\" applicationTheme=\"light\" (selectedTab)=\"onTabChange($event)\">\n <ng-template uiTab tabLabel=\"Details\" tabName=\"details\"> Content for the Details tab </ng-template>\n <ng-template uiTab tabLabel=\"History\" tabName=\"history\" iconLeft=\"Clock\">\n Content for the History tab with an icon\n </ng-template>\n <ng-template uiTab tabLabel=\"Settings\" tabName=\"settings\" [disabled]=\"!hasAccess\">\n Content for the Settings tab\n </ng-template>\n</ui-tabs>\n```\n\n`(selectedTab)` emits the selected `Tab`; `(selectedTabIndex)` emits its position.\n\nThe tabs component supports different styles (underlined or filled), icons, and can disable individual tabs.\n\n### Selecting a tab\n\nBind `[selectedTabName]` to open a specific tab — a deep link, a query param, a \"go to results\" action:\n\n```html\n<ui-tabs [selectedTabName]=\"activeTab()\" (selectedTab)=\"onTabChange($event)\">\n <!-- ... -->\n</ui-tabs>\n```\n\nThe name is applied as tabs register, so it works even when the group renders after the first change\ndetection pass — inside `@defer`, `@if (loaded())` or a translation scope that resolves asynchronously.\nPrefer it over calling `selectTab()` from `ngAfterViewInit`, which is silently dropped if the group or\nits tabs do not exist yet.\n\n`[selectedTabName]` is a request, not live state. It is applied when the value changes and once more as\nsoon as the named tab registers, but a later click will not be pulled back to it, and an unknown name is\na no-op. Applying it emits nothing and does not consult the current tab's `canLeave` — the value is the\nconsumer's own decision, so the consumer owns any guarding around it. User-driven switches still honour\n`canLeave`.\n\n`selectTab(tabName)` remains available on the component instance for jumping to a tab in response to a\nuser action.\n\n### Showing a loading state on one tab\n\nWhen a value the label depends on is still loading — a count, a total — set `[isLabelLoading]` on that tab\nto show a small spinner after its label:\n\n```html\n<ng-template\n uiTab\n tabName=\"skills-tests\"\n [tabLabel]=\"'Skills tests' + (count() === undefined ? '' : ' (' + count() + ')')\"\n [isLabelLoading]=\"count() === undefined\"\n>\n …\n</ng-template>\n```\n\nThis is per tab. It is different from `[isLoading]` on `ui-tabs`, which replaces **every** tab label with a\nskeleton while the whole group loads — use that when nothing is known yet, and `isLabelLoading` when the\ntabs exist but one label is still filling in.\n\nThe spinner is `aria-hidden`, and the loading state is announced through the tab's own `aria-label`\ninstead — a spinner is a live region, and nesting one inside `role=\"tab\"` would announce on every render.\n\n### Tab content state across a switch\n\nTab content state is kept for you. Material _detaches_ an inactive tab's view rather than destroying it,\nso component state, form values and the DOM nodes themselves survive a tab switch — filters, search text\nand loaded results are still there when the user comes back. You do not need to do anything for this.\n\nWhat leaving a tab does change is DOM presence: the inactive tab's content is removed from the document,\nso while it is inactive it is not reachable by find-in-page, the accessibility tree or\n`document.querySelector`, and layout queries against it read `0`. If you have content that misbehaves\nwhile out of the document — something that reloads or resets on re-insertion, or that measures its own\nlayout — raise it with the Canopy UI team rather than reaching into `tabGroup` directly.\n","whenToUse":["**Parallel, repeatable switching** — use Tabs when the sections are siblings, not steps, and users may want to move between them repeatedly","**Tight vertical space** — use the 32px small variant inside Cards or panels where space is limited","**Sensible default** — always pre-select the most relevant tab for the context"],"whenNotToUse":["**Sequential flows that must be completed in order** → use Stepper instead","**More than 5–6 tabs** → a side navigation or filter approach scales better beyond that","**Content the user will always need** → if they need all sections, show them all rather than hiding them behind Tabs"],"template":"<mat-tab-group\n #tabGroup\n headerPosition=\"above\"\n class=\"tabs-container\"\n [ngClass]=\"['tabs-type-' + ((isMobile$ | async) ? 'underlined' : type), 'tabs-header-size-' + tabHeaderSize()]\"\n [class.tabs-container-loading]=\"isLoading()\"\n [attr.theme]=\"applicationTheme\"\n [dynamicHeight]=\"dynamicHeight\"\n [selectedIndex]=\"tabIndex()\"\n [animationDuration]=\"animationDuration\"\n (selectedIndexChange)=\"onTabChange($event)\"\n role=\"tablist\"\n>\n @for (tab of tabs; track trackByTabName(i, tab); let i = $index) {\n <mat-tab [disabled]=\"tab.disabled ?? false\" [attr.aria-label]=\"tab?.ariaLabel\">\n <ng-template mat-tab-label>\n <a\n data-test-role=\"tab-link\"\n [attr.data-testid]=\"tab.tabName\"\n class=\"tab-name\"\n [href]=\"tab?.linkUrl ? tab.linkUrl : '#'\"\n (click)=\"$event.preventDefault()\"\n [attr.aria-selected]=\"i === tabIndex()\"\n [attr.aria-label]=\"\n tab.tabLabel +\n (tab.isLabelLoading ? ', ' + ('COMMON.LOADING' | uiTranslate | async) : '') +\n (i === tabIndex() ? ', ' + ('TABS.SELECTED' | uiTranslate | async) : '')\n \"\n role=\"tab\"\n >\n @if (isLoading()) {\n <ui-skeleton\n [count]=\"1\"\n [theme]=\"tab.skeletonTheme ?? {}\"\n [isAiTheme]=\"!!tab.isSkeletonAiTheme\"\n ></ui-skeleton>\n } @else {\n @if (tab?.iconLeft) {\n <ui-icon\n color=\"rebrand-black\"\n [size]=\"'24'\"\n [tabindex]=\"1\"\n class=\"left-icon\"\n [name]=\"tab.iconLeft!\"\n ></ui-icon>\n }\n <span class=\"label\">{{ tab.tabLabel }}</span>\n @if (tab.isLabelLoading) {\n <span class=\"label-spinner\" aria-hidden=\"true\">\n <ui-spinner size=\"small\" [isLoader]=\"false\" [applicationTheme]=\"applicationTheme\"></ui-spinner>\n </span>\n }\n @if (tab?.iconRight) {\n <ui-icon color=\"rebrand-black\" [size]=\"'24'\" class=\"right-icon\" [name]=\"tab.iconRight!\"></ui-icon>\n }\n }\n </a>\n </ng-template>\n <div role=\"tabpanel\" [attr.id]=\"'tabpanel-' + i\" [attr.aria-labelledby]=\"'tab-' + i\">\n <ng-container *ngTemplateOutlet=\"tab.contentTemplateRef\"></ng-container>\n </div>\n </mat-tab>\n }\n</mat-tab-group>\n","queryGuide":{"roles":["tablist","tab-link","tab","tabpanel"],"ariaAttributes":["aria-hidden","aria-label","aria-selected","aria-labelledby"],"dataTestIds":[],"materialComponents":["mat-tab-group","mat-tab"],"childUIComponents":["ui-skeleton","ui-icon","ui-spinner"],"suggestedQueries":["screen.getByRole('tablist')","screen.getByRole('tab-link')","screen.getByRole('tab')","screen.getByRole('tabpanel')","screen.getByLabelText('label text')"]},"filePath":"components/tabs/tabs.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=2395-21762","lastModified":1786626756421.166},"tag":{"name":"TagComponent","selector":"ui-tag","category":"tag","description":"An interactive, removable label used to display and manage a set of selected values — skills, job categories, filters applied, or custom labels. Unlike Badge (read-only status), a Tag is actionable: users can remove it by clicking the × control. Used in Autocomplete selected state, filter chips, and keyword tagging in assessment settings.","importPath":"@testgorilla/tgo-ui/components/tag","moduleName":"TagComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Tag's label","isSignal":true},{"name":"icon","type":"IconName","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Icon","isSignal":true},{"name":"allowClose","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether to allow the tag to be closed.","isSignal":true},{"name":"readOnly","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the tag is in read-only mode.","isSignal":true},{"name":"showIconWhenSelected","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Display icon when is selected","isSignal":true},{"name":"pinnedBackground","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Pins the tag to its filled background at rest, without tying that background\nto the selected state — pressing the tag never flips its fill. Interactive\nstates (hover, focus ring, press scale) and the `press` output are unaffected,\nso the tag stays clickable; use `readOnly` when it should not be.\n\nThe fill follows the theme: grayscale in `light` / `dark`, petrol in `classic`.","isSignal":true},{"name":"isDisabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Specifies whether the element is disabled.","isSignal":true},{"name":"applicationTheme","type":"ApplicationTheme | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":true},{"name":"ariaLabel","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":true},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":true},{"name":"showBadge","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether to show the badge on the tag.","isSignal":true},{"name":"notificationsAmount","type":"number","defaultValue":"0","required":false,"alias":null,"transform":null,"description":"The notifications amount to be displayed on the badge.","isSignal":true},{"name":"isSelected","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Whether the tag is selected.","isSignal":true}],"outputs":[{"name":"close","eventType":"void","alias":null,"description":"Event triggered when the tag should be closed.","isSignal":true},{"name":"press","eventType":"boolean","alias":null,"description":"Event triggered when a press action occurs.","isSignal":true},{"name":"isSelectedChange","eventType":"boolean","alias":null,"description":"Two-way binding change event for isSelected","isSignal":true}],"module":{"name":"TagComponentModule","imports":["CommonModule","IconComponentModule","TooltipComponentModule","MatTooltipModule","MatInputModule","UiTranslatePipe","TagComponent"],"exports":["TagComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"TagColor","value":"'teal' | 'petrol' | 'grey' | 'red' | 'gold'"}],"interfaces":[],"enums":[]},"examples":[{"name":"TruncatedLabel","template":"<ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" pinnedBackground=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" [isSelected]=\"true\" showIconWhenSelected=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" allowClose=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" readOnly=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" icon=\"Folder\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" icon=\"Assessment\" allowClose=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" label=\"Long label to show how truncated text looks like\" icon=\"Sparkle-in-line\" showBadge=\"true\" notificationsAmount=\"0\" allowClose=\"true\"></ui-tag>","props":{}},{"name":"Disabled","template":"<ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Suggestion\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Suggestion gray\" pinnedBackground=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Filter(Selected)\" [isSelected]=\"true\" showIconWhenSelected=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Input Without Icon\" allowClose=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Read-only\" readOnly=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Suggestion With Icon\" icon=\"Folder\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"Input With Icon\" icon=\"Assessment\" allowClose=\"true\"></ui-tag>\n <ui-tag applicationTheme=\"${appTheme}\" isDisabled=\"true\" label=\"With Notifications Badge\" showBadge=\"true\" notificationsAmount=\"5\"></ui-tag>","props":{}}],"documentation":"An interactive, removable label used to display and manage a set of selected values — skills, job categories, filters applied, or custom labels. Unlike Badge (read-only status), a Tag is actionable: users can remove it by clicking the × control. Used in Autocomplete selected state, filter chips, and keyword tagging in assessment settings.\n\n## When to use\n\n- **User-selected values** — represent a value the user has chosen and should be able to remove\n- **Horizontal wrapping row** — group Tags so they flow naturally when the list is long\n- **Clear, concise labels** — keep label text scannable at a glance\n\n## When NOT to use\n\n- **Read-only status** → don't use Tag for this; use Badge instead\n- **Truncated labels without a tooltip** → avoid hiding text, since the label is the only information the user has\n- **Primary navigation** → don't make Tags the main nav mechanism; they're for filtering and labelling, not page-level nav\n\n## How to use\n\nTo use the Tag component, you need to import the `TagComponentModule` in your module:\n\n```typescript\nimport { TagComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n TagComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-tag\n label=\"Category\"\n [icon]=\"'Folder'\"\n [isSelected]=\"false\"\n [allowClose]=\"true\"\n applicationTheme=\"light\"\n (closeClicked)=\"onTagClose()\"\n (tagClicked)=\"onTagClick()\"\n>\n</ui-tag>\n```\n\nThe tag component can be used for filtering, selections, or input chips.\n\n### Gray suggestion\n\nSet `pinnedBackground` for a suggestion tag that sits on the filled background at rest and keeps it when pressed:\n\n```html\n<ui-tag label=\"Category\" pinnedBackground (press)=\"onTagClick()\"></ui-tag>\n```\n\nIt reads like a selected filter but is decoupled from `isSelected`, so clicking never flips the background between the outlined and filled styles. Hover, the focus ring, the press scale, and the `press` output all behave as they do on a regular suggestion tag.\n\nReach for `pinnedBackground` when the tag should look settled rather than selectable — a filter that is already applied elsewhere, or a label that carries a click action but no on/off state. Use `isSelected` with `showIconWhenSelected` instead when the tag genuinely toggles, and `readOnly` when it should not be interactive at all.\n\nThe fill follows the theme: grayscale in `light` and `dark`, petrol in `classic`.\n","whenToUse":["**User-selected values** — represent a value the user has chosen and should be able to remove","**Horizontal wrapping row** — group Tags so they flow naturally when the list is long","**Clear, concise labels** — keep label text scannable at a glance"],"whenNotToUse":["**Read-only status** → don't use Tag for this; use Badge instead","**Truncated labels without a tooltip** → avoid hiding text, since the label is the only information the user has","**Primary navigation** → don't make Tags the main nav mechanism; they're for filtering and labelling, not page-level nav"],"template":"<div class=\"tag-wrapper\">\n <div\n [matTooltip]=\"(isEllipseActiveObs$ | async) ? label() : ''\"\n [matTooltipClass]=\"theme()\"\n [tabindex]=\"tabIndex()\"\n class=\"tag-container\"\n [attr.data-testid]=\"'tag--container'\"\n [attr.theme]=\"theme()\"\n [class]=\"{\n filled: filled(),\n readonly: readOnly(),\n disabled: isDisabled(),\n outlined: !isSelected(),\n shrink,\n }\"\n (click)=\"onPress()\"\n (mouseenter)=\"setHoverState(true)\"\n (mouseleave)=\"setHoverState(false)\"\n (mousedown)=\"setShrinkState(true)\"\n (mouseup)=\"setShrinkState(false)\"\n (touchstart)=\"setShrinkState(true)\"\n (touchend)=\"setShrinkState(false)\"\n (keydown)=\"onKeydown($event)\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n [attr.aria-required]=\"ariaRequired()\"\n >\n @if (icon()) {\n <ui-icon class=\"icon\" [color]=\"iconColor\" [name]=\"iconToPass()\" [applicationTheme]=\"theme()\"></ui-icon>\n }\n @if (isSelected() && showIconWhenSelected()) {\n <ui-icon [color]=\"iconColor\" class=\"icon\" [name]=\"'Check'\" [applicationTheme]=\"theme()\"></ui-icon>\n }\n <span class=\"tag-label\" #labelElement>{{ label() }}</span>\n @if (showBadge()) {\n <span class=\"count\" aria-hidden=\"true\">\n <span class=\"count-dot\">&bull;</span>\n <span class=\"count-value\">{{ notificationsAmount() }}</span>\n </span>\n }\n @if (allowClose()) {\n <ui-icon\n (focus)=\"closeBtnFocused.set(true)\"\n (blur)=\"closeBtnFocused.set(false)\"\n [tabindex]=\"tabIndex()\"\n class=\"delete-icon\"\n [attr.aria-label]=\"'TAG.REMOVE' | uiTranslate | async\"\n [color]=\"iconColor\"\n (click)=\"onClose(); $event.stopPropagation()\"\n [name]=\"'Close'\"\n [applicationTheme]=\"theme()\"\n ></ui-icon>\n }\n </div>\n</div>\n","queryGuide":{"roles":[],"ariaAttributes":["aria-hidden","aria-label","aria-required"],"dataTestIds":["tag--container"],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":["screen.getByLabelText('label text')","screen.getByTestId('tag--container')"]},"filePath":"components/tag/tag.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=22850-6766","lastModified":1786626756421.6294},"toggle":{"name":"ToggleComponent","selector":"ui-toggle","category":"toggle","description":"A binary on/off switch for settings and preferences. Communicates an immediate, reversible state change without requiring a separate save action. Used in assessment settings (enable/disable proctoring, webcam, time limits), account preferences, and notification controls.","importPath":"@testgorilla/tgo-ui/components/toggle","moduleName":"ToggleComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"selected","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"The `selected` property determines the current state of the toggle switch.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"The `disabled` property determines whether the toggle switch is disabled.","isSignal":false},{"name":"label","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Toggle label","isSignal":false},{"name":"message","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"Message","isSignal":false},{"name":"maxWidth","type":"number | undefined","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"Max width of the toggle container","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"ariaLabel","type":"string","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"A string representing the ARIA label for accessibility.\nThis label is used to provide an accessible name for the input element.","isSignal":false},{"name":"ariaRequired","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false},{"name":"preventClick","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"disable/enable click on toggle","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"The loading state","isSignal":false},{"name":"ariaDescribedby","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"A string representing the ARIA requirement for accessibility.\nThis attribute is used to indicate whether an input field is required for form submission.","isSignal":false}],"outputs":[{"name":"toggle","eventType":"boolean","alias":null,"description":"The `toggle` event is triggered when the state of the toggle switch changes. It returns the new state.","isSignal":false}],"module":{"name":"ToggleComponentModule","imports":["CommonModule","MatTooltipModule","EllipseTextDirective","MatInputModule","UiTranslatePipe","IconComponentModule","SpinnerComponentModule","ToggleComponent"],"exports":["ToggleComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"RegisterFn","value":"(value?: boolean) => void"}],"interfaces":[],"enums":[]},"examples":[{"name":"WithReactiveForm","template":"<span>Switch Value: {{form.controls.switch.value | json}}</span>\n <form [formGroup]=\"form\">\n <ui-toggle formControlName=\"switch\" applicationTheme=\"${appTheme}\"></ui-toggle>\n </form>","props":{}}],"documentation":"A binary on/off switch for settings and preferences. Communicates an immediate, reversible state change without requiring a separate save action. Used in assessment settings (enable/disable proctoring, webcam, time limits), account preferences, and notification controls.\n\n## When to use\n\n- **Immediate effect** — use Toggle for settings that take effect right away; if the change requires saving, use a Checkbox with a Save button instead\n- **Clear label** — always pair with a label that describes what the toggle controls\n- **On state prominence** — use the On state as the visually prominent state (pink track) to signal that something is active\n\n## When NOT to use\n\n- **Multi-option selections** → use Radio Buttons or a Segmented Button instead\n- **Forms that require a submit action** → the on/off affordance implies immediacy, so don't place Toggle inside one\n- **Colour alone for state** → don't rely on it; the label should always reflect current state\n\n## How to use\n\nTo use the Toggle component, you need to import the `ToggleComponentModule` in your module:\n\n```typescript\nimport { ToggleComponentModule } from '@testgorilla/tgo-ui';\n\n@NgModule({\n imports: [\n // ... other imports\n ToggleComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-toggle\n [selected]=\"isToggleSelected\"\n [label]=\"'Toggle Switch'\"\n (selectedChange)=\"handleToggleChange($event)\"\n applicationTheme=\"light\"\n>\n</ui-toggle>\n```\n","whenToUse":["**Immediate effect** — use Toggle for settings that take effect right away; if the change requires saving, use a Checkbox with a Save button instead","**Clear label** — always pair with a label that describes what the toggle controls","**On state prominence** — use the On state as the visually prominent state (pink track) to signal that something is active"],"whenNotToUse":["**Multi-option selections** → use Radio Buttons or a Segmented Button instead","**Forms that require a submit action** → the on/off affordance implies immediacy, so don't place Toggle inside one","**Colour alone for state** → don't rely on it; the label should always reflect current state"],"template":"<div class=\"toggle\" [ngClass]=\"classes\">\n <div\n [style.--max-width]=\"maxWidth + 'px'\"\n tabindex=\"0\"\n class=\"toggle-container\"\n [ngClass]=\"{ active: selected }\"\n (click)=\"onToggle()\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-required]=\"ariaRequired\"\n (keydown)=\"onKeydown($event)\"\n role=\"switch\"\n [attr.aria-labelledby]=\"'label'\"\n [attr.aria-describedby]=\"'ariaDescribedby'\"\n [attr.aria-checked]=\"selected\"\n >\n <span [style.display]=\"'none'\" id=\"ariaDescribedby\">{{ ariaDescribedby }}</span>\n <div\n class=\"label\"\n uiEllipseText\n [matTooltipShowDelay]=\"800\"\n [matTooltip]=\"showLabelTooltip ? label : ''\"\n (onChangeTextState)=\"changeTextState($event)\"\n id=\"label\"\n >\n {{ label }}\n </div>\n @if (!loading) {\n <div class=\"knob\" [ngClass]=\"{ selected }\">\n @if (selected && !label) {\n <ui-icon [color]=\"disabled ? 'grey' : 'white'\" [size]=\"'16'\" [name]=\"'Check-round-filled'\"></ui-icon>\n }\n </div>\n } @else {\n <ui-spinner [isLoader]=\"false\" [size]=\"'small'\" [applicationTheme]=\"'dark'\"></ui-spinner>\n }\n </div>\n @if (message && selected) {\n <div class=\"message\">{{ message }}</div>\n }\n</div>\n","queryGuide":{"roles":["switch"],"ariaAttributes":["aria-label","aria-required","aria-labelledby","aria-describedby","aria-checked"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon","ui-spinner"],"suggestedQueries":["screen.getByRole('switch')","screen.getByLabelText('label text')"]},"filePath":"components/toggle/toggle.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=422-23558","lastModified":1786626756422.0789},"tooltip":{"name":"TooltipComponent","selector":"ui-tooltip","category":"tooltip","description":"A small floating label that appears on hover (or focus) to provide supplementary information about an element. Tooltips are read-only and non-interactive. Used to label Icon Buttons, explain truncated text, and provide additional context for form fields or data points.","importPath":"@testgorilla/tgo-ui/components/tooltip","moduleName":"TooltipComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"position","type":"TooltipPositionType","defaultValue":"TooltipPositionType.TOP","required":false,"alias":null,"transform":null,"description":"Allows the user to define the position of the tooltip relative to the parent element.","isSignal":false},{"name":"disabled","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":"booleanAttribute","description":"Disables the display of the tooltip.","isSignal":false},{"name":"message","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The message to be displayed in the tooltip.","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"labelForDefinition","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"The label for definition variant","isSignal":false},{"name":"definitionTooltipSize","type":"TooltipDefinitionSize","defaultValue":"'medium'","required":false,"alias":null,"transform":null,"description":"Defines size for definition variant","isSignal":false},{"name":"variant","type":"TooltipVariant","defaultValue":"'basic'","required":false,"alias":null,"transform":null,"description":"Variant render tooltip","isSignal":false}],"outputs":[],"module":{"name":"TooltipComponentModule","imports":["MatTooltipModule","CommonModule","TooltipTemplateDirective","EllipseTextDirective","UiTranslatePipe","TooltipComponent"],"exports":["TooltipComponent","TooltipTemplateDirective"],"declarations":[],"providers":["{ provide: 'TooltipComponentToken', useValue: TooltipComponent }"]},"types":{"typeAliases":[{"name":"TooltipVariant","value":"'basic' | 'definition'"},{"name":"TooltipDefinitionSize","value":"'small' | 'medium' | 'large'"}],"interfaces":[],"enums":[{"name":"TooltipPositionType","members":[{"name":"TOP","value":"'top'"},{"name":"BOTTOM","value":"'bottom'"},{"name":"LEFT","value":"'left'"},{"name":"RIGHT","value":"'right'"}]}]},"examples":[{"name":"Default","template":"<div class=\"example-container\">\n <ui-tooltip [position]=\"position\" [message]=\"message\" applicationTheme=\"${appTheme}\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </ui-tooltip>\n </div>","props":{"message":"'Tooltip message for default tooltip'","position":"TooltipPositionType.TOP"}},{"name":"WithBottom","template":"<div class=\"example-container\">\n <ui-tooltip [position]=\"position\" [message]=\"message\" applicationTheme=\"${appTheme}\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n size=\"small\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </ui-tooltip>\n </div>","props":{"message":"'Message for bottom tooltip'","position":"TooltipPositionType.TOP"}},{"name":"WithRight","template":"<div class=\"example-container\">\n <ui-tooltip [position]=\"position\" [message]=\"message\" applicationTheme=\"${appTheme}\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n size=\"small\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </ui-tooltip>\n </div>","props":{"message":"'Message for bottom tooltip'","position":"TooltipPositionType.RIGHT"}},{"name":"WithLeft","template":"<div class=\"example-container\">\n <ui-tooltip [position]=\"position\" [message]=\"message\" applicationTheme=\"${appTheme}\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n size=\"small\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </ui-tooltip>\n </div>","props":{"message":"'Message for bottom tooltip'","position":"TooltipPositionType.LEFT"}},{"name":"WithDirective","template":"<div class=\"example-container left\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"primary\"\n iconName=\"Help\"\n iconPosition=\"left\"\n label=\"More Help\"\n [matTooltip]=\"message\"\n matTooltipPosition=\"below\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </div>\n <div class=\"example-container\">\n <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse molestie est ipsum, nec <a [style.color]=\"'${linkColor}'\" matTooltipClass=\"${appTheme}\" href=\"#\" matTooltipPosition=\"above\" matTooltip=\"Tooltip for link\">lobortis neque lacinia</a> ac. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In efficitur urna quis ultricies mattis. Cras quis euismod urna. Duis facilisis ac mauris in gravida.</p>\n </div>","props":{"message":"'Message for bottom tooltip'","position":"TooltipPositionType.LEFT"}},{"name":"WithLongTextLeft","template":"<div class=\"example-container\">\n <ui-tooltip [position]=\"position\" [message]=\"message\" applicationTheme=\"${appTheme}\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n size=\"small\"\n matTooltipClass=\"${appTheme}\"\n applicationTheme=\"${appTheme}\"\n ></ui-button>\n </ui-tooltip>\n </div>","props":{"message":"'Message for bottom tooltip'","position":"TooltipPositionType.LEFT"}},{"name":"Templ","template":"<div class=\"example-container\">\n <ui-button\n data-testid=\"tooltip\"\n type=\"secondary\"\n [justIcon]=\"true\"\n iconName=\"Help\"\n label=\"secondary\"\n size=\"small\"\n [uiTooltipTemplate]=\"template\"\n [tooltipPosition]=\"'right'\"\n applicationTheme=\"${appTheme}\">\n </ui-button>\n\n <ng-template #template>\n <div style=\"display: flex; flex-direction: column;\">\n <ui-button [label]=\"'Test'\"></ui-button>\n </div>\n </ng-template>\n </div>","props":{}},{"name":"Definition","template":"<div><ui-tooltip data-testid=\"tooltip\" [variant]=\"variant\" [labelForDefinition]=\"labelForDefinition\" [message]=\"message\" [definitionTooltipSize]=\"definitionTooltipSize\"></ui-tooltip></div>","props":{"definitionTooltipSize":"'medium'","message":"'Tooltip message'","position":"TooltipPositionType.LEFT","variant":"'definition'","labelForDefinition":"\"Long label that should go to the tooltip message when it's get truncated. Just shrink the screen to see it in action\""}}],"documentation":"A small floating label that appears on hover (or focus) to provide supplementary information about an element. Tooltips are read-only and non-interactive. Used to label Icon Buttons, explain truncated text, and provide additional context for form fields or data points.\n\n## When to use\n\n- **Every Icon Button** — an icon with no label is inaccessible without one\n- **Short content** — a label, a brief phrase, or a concise explanation, not full sentences\n- **Truncated text in tables** — use the Definition variant to reveal the full string on hover\n\n## When NOT to use\n\n- **Interactive elements (links, buttons)** → don't put them inside a Tooltip, use a Popover instead\n- **Essential information** → content users must read to proceed shouldn't hide behind a hover\n- **Mobile without a touch trigger** → don't show Tooltips where hover doesn't exist without a separate touch-trigger fallback\n\n## How to use\n\nTo use the Tooltip component, you need to import the `TooltipComponentModule` in your module:\n\n```typescript\nimport { TooltipComponentModule } from '@testgorilla/tgo-ui/components/tooltip';\n\n@NgModule({\n imports: [\n // ... other imports\n TooltipComponentModule,\n ],\n // ... declarations, providers, etc.\n})\nexport class YourModule {}\n```\n\nThen you can use the component in your templates:\n\n```html\n<ui-tooltip position=\"top\" message=\"This is a tooltip message\" applicationTheme=\"light\">\n <button>Hover me</button>\n</ui-tooltip>\n```\n\nYou can also use the directive form:\n\n```html\n<button matTooltip=\"This is a tooltip message\" matTooltipPosition=\"above\" matTooltipClass=\"light\">Hover me</button>\n```\n\nFor more complex content, you can use a template:\n\n```html\n<button [uiTooltipTemplate]=\"myTemplate\" [tooltipPosition]=\"'right'\">Hover for complex tooltip</button>\n\n<ng-template #myTemplate>\n <div>Custom tooltip content</div>\n <button>With interactive elements</button>\n</ng-template>\n```\n\nThe tooltip component supports different positions and can contain any HTML content.\n","whenToUse":["**Every Icon Button** — an icon with no label is inaccessible without one","**Short content** — a label, a brief phrase, or a concise explanation, not full sentences","**Truncated text in tables** — use the Definition variant to reveal the full string on hover"],"whenNotToUse":["**Interactive elements (links, buttons)** → don't put them inside a Tooltip, use a Popover instead","**Essential information** → content users must read to proceed shouldn't hide behind a hover","**Mobile without a touch trigger** → don't show Tooltips where hover doesn't exist without a separate touch-trigger fallback"],"template":"@if (variant === 'definition') {\n <div\n [tabindex]=\"0\"\n class=\"definition-tooltip\"\n [attr.aria-label]=\"labelForDefinition + ', ' + ('TOOLTIP.DETAILS' | uiTranslate | async) + ':' + message\"\n style=\"position: relative\"\n [tooltipPosition]=\"position\"\n [uiTooltipTemplate]=\"dif\"\n >\n <span\n class=\"label-definition-outside\"\n [ngClass]=\"'tooltip-size-' + definitionTooltipSize\"\n uiEllipseText\n (onChangeTextState)=\"isLabelEllipse = $event\"\n >\n {{ labelForDefinition }}\n </span>\n </div>\n\n <ng-template #dif>\n <div [attr.aria-label]=\"labelForDefinition + ' ' + message\"></div>\n @if (isLabelEllipse) {\n <div class=\"label-definition-inner\">\n {{ labelForDefinition }}\n </div>\n }\n <div class=\"text-definition\">\n {{ message }}\n </div>\n </ng-template>\n} @else {\n <div\n [matTooltip]=\"message\"\n [matTooltipDisabled]=\"disabled\"\n [matTooltipPosition]=\"matPosition\"\n [matTooltipClass]=\"applicationTheme\"\n [attr.aria-label]=\"message\"\n tabindex=\"0\"\n style=\"position: relative\"\n >\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">{{ message }}</div>\n <ng-content></ng-content>\n @if (templateRef) {\n <div [class]=\"'template-tooltip'\">\n <ng-container *ngTemplateOutlet=\"templateRef\"></ng-container>\n </div>\n }\n </div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":["aria-label","aria-live"],"dataTestIds":[],"materialComponents":[],"childUIComponents":[],"suggestedQueries":["screen.getByLabelText('label text')"]},"filePath":"components/tooltip/tooltip.component.ts","supportsTheme":true,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=519-7957","lastModified":1786626756422.628},"universal-skills":{"name":"UniversalSkillsReportComponent","selector":"ui-universal-skills-report","category":"universal-skills","description":"UniversalSkillsReportComponent component","importPath":"@testgorilla/tgo-ui/components/universal-skills","moduleName":"UniversalSkillsReportComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"skillAreaDataList","type":"SkillAreaData[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The list of items to be used","isSignal":false}],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"@for (skillAreaGroup of skillAreaGroupMap | keyvalue; track skillAreaGroup) {\n <div class=\"universal-skills-report\">\n <section class=\"title-and-info\">\n <h4>{{ skillAreaGroup.value.title | uiTranslate | async }}</h4>\n <span>{{ skillAreaGroup.value.description | uiTranslate | async }}</span>\n </section>\n <section class=\"accordion-items\">\n @for (skillAreaData of skillAreaGroup?.value?.skillAreaList; track skillAreaData) {\n <ui-accordion class=\"universal-skills-accordion\" label=\"-\" [allowCustomHeader]=\"true\">\n <div customHeader class=\"custom-header\">\n <h4 class=\"custom-header-subtitle\">\n {{\n translationContext + 'SKILL_AREA_TYPES.' + skillAreaData.skillAreaName + '.TITLE' | uiTranslate | async\n }}\n </h4>\n <div class=\"inner-container\">\n <ui-badge\n class=\"percentile-scoring-badge\"\n [label]=\"\n (skillAreaData.percentileScoring | memoizeFunc: getScoreBadgeLabel : this | uiTranslate | async) ||\n 'neutral'\n \"\n [rebrandColor]=\"skillAreaData.percentileScoring | memoizeFunc: getScoreBadgeColor : this\"\n [applicationTheme]=\"'light'\"\n ></ui-badge>\n <h5 class=\"percentile-quantity\">\n {{ skillAreaData?.percentileScoring }}\n <sup class=\"percentile-suffix\">{{\n skillAreaData.percentileScoring | ordinalSuffix | uiTranslate | async\n }}</sup>\n {{ translationContext + 'PERCENTILE' | uiTranslate | async }}\n </h5>\n <ui-progress-bar\n class=\"percentile-scoring-bar\"\n mode=\"determinate\"\n [progress]=\"skillAreaData.percentileScoring\"\n ></ui-progress-bar>\n </div>\n </div>\n <div class=\"content-container\">\n <p class=\"skill-description\">\n {{\n translationContext + 'SKILL_AREA_TYPES.' + skillAreaData.skillAreaName + '.DESCRIPTION'\n | uiTranslate\n | async\n }}\n </p>\n <h5>\n {{ skillAreaData.percentileScoring | memoizeFunc: getScoreBadgeLabel : this | uiTranslate | async }} score\n </h5>\n <div class=\"inner-container\">\n <p>\n {{\n translationContext +\n 'SKILL_AREA_TYPES.' +\n skillAreaData.skillAreaName +\n '.' +\n ([skillAreaData.percentileScoring, true] | memoizeFunc: checkScoreClassification : this) +\n '.GENERAL'\n | uiTranslate\n | async\n }}\n </p>\n </div>\n <div class=\"inner-container\">\n <p>\n {{\n translationContext +\n 'SKILL_AREA_TYPES.' +\n skillAreaData.skillAreaName +\n '.' +\n ([skillAreaData.percentileScoring, true] | memoizeFunc: checkScoreClassification : this) +\n '.WORK_ENVIRONMENT'\n | uiTranslate\n | async\n }}\n </p>\n </div>\n <div class=\"inner-container\">\n <p>\n {{\n translationContext +\n 'SKILL_AREA_TYPES.' +\n skillAreaData.skillAreaName +\n '.' +\n ([skillAreaData.percentileScoring, true] | memoizeFunc: checkScoreClassification : this) +\n '.RECOMMENDATIONS'\n | uiTranslate\n | async\n }}\n </p>\n </div>\n </div>\n </ui-accordion>\n }\n </section>\n </div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-accordion","ui-badge","ui-progress-bar"],"suggestedQueries":[]},"filePath":"components/universal-skills/universal-skills.component.ts","supportsTheme":true,"lastModified":1786626756423.3997},"spider-charts":{"name":"UniversalSkillsSpiderChartsComponent","selector":"ui-universal-skills-spider-charts","category":"spider-charts","description":"UniversalSkillsSpiderChartsComponent component","importPath":"@testgorilla/tgo-ui/components/spider-charts","moduleName":"UniversalSkillsSpiderChartsComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"skillAreaDataList","type":"SkillAreaData[]","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"The list of items to be used","isSignal":false},{"name":"loading","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Defines if the component is loading","isSignal":false},{"name":"size","type":"'small' | 'large'","defaultValue":"'large'","required":false,"alias":null,"transform":null,"description":"Defines the size of the spider chart","isSignal":false}],"outputs":[],"module":{"name":"","imports":[],"exports":[],"declarations":[],"providers":[]},"types":{"typeAliases":[],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"@for (spiderChartData of skillAreaSpiderChartData; track spiderChartData) {\n <ui-accordion\n class=\"universal-skills-spider-charts\"\n [label]=\"(spiderChartData.title | uiTranslate | async) || ''\"\n variant=\"filter\"\n >\n <ui-spider-chart\n [spiderChartData]=\"spiderChartData.spiderChartData\"\n [loading]=\"loading\"\n [size]=\"size\"\n ></ui-spider-chart>\n </ui-accordion>\n @if ($index < skillAreaSpiderChartData.length - 1) {\n <ui-divider class=\"universal-skills-spider-charts-divider\" size=\"small\"></ui-divider>\n }\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-accordion","ui-spider-chart","ui-divider"],"suggestedQueries":[]},"filePath":"components/spider-charts/spider-charts.component.ts","supportsTheme":true,"lastModified":1786626756423.0945},"validation-error":{"name":"ValidationErrorComponent","selector":"ui-validation-error","category":"validation-error","description":"ValidationErrorComponent component","importPath":"@testgorilla/tgo-ui/components/validation-error","moduleName":"ValidationErrorModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"ngControl","type":"NgControl","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"touchedOn","type":"boolean","defaultValue":"true","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"errorMessage","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"label","type":"string | undefined | null","defaultValue":null,"required":false,"alias":null,"transform":null,"description":"","isSignal":false},{"name":"applicationTheme","type":"ApplicationTheme","defaultValue":"'light'","required":false,"alias":null,"transform":null,"description":"Defines the application theme","isSignal":false},{"name":"customErrors","type":"Record<string, string>","defaultValue":"{}","required":false,"alias":null,"transform":null,"description":"Maps validator error keys to display message templates. Supports {{placeholder}} interpolation.","isSignal":false}],"outputs":[],"module":{"name":"ValidationErrorModule","imports":["CommonModule","IconComponentModule","UiTranslatePipe","ValidationErrorComponent"],"exports":["ValidationErrorComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"ErrorType","value":"'maxlength' | 'minlength' | 'min' | 'max'"}],"interfaces":[],"enums":[]},"examples":[],"documentation":"","whenToUse":[],"whenNotToUse":[],"template":"@for (key of customErrorKeys; track $index) {\n @if (ngControl?.hasError(key) && (touchedOn ? ngControl?.touched : true)) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ resolveErrorMessage(key) }}</span>\n </div>\n }\n}\n\n@if (ngControl?.hasError('maxlength') && !customErrors['maxlength']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{\n translationContextErrors + 'MAX_LENGTH' | uiTranslate: { maxlength: getErrorValue('maxlength') } | async\n }}</span>\n </div>\n}\n\n@if (ngControl?.hasError('minlength') && !customErrors['min']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{\n translationContextErrors + 'MIN_LENGTH' | uiTranslate: { maxlength: getErrorValue('minlength') } | async\n }}</span>\n </div>\n}\n\n@if (ngControl?.hasError('min') && !customErrors['min']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ translationContextErrors + 'MIN' | uiTranslate: { min: getErrorValue('min') } | async }}</span>\n </div>\n}\n\n@if (ngControl?.hasError('max') && !customErrors['max']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ translationContextErrors + 'MAX' | uiTranslate: { max: getErrorValue('max') } | async }}</span>\n </div>\n}\n\n@if (ngControl?.hasError('required') && (touchedOn ? ngControl?.touched : true) && !customErrors['required']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{\n translationContextErrors + (label ? 'REQUIRED_FIELD' : 'REQUIRED') | uiTranslate: { label } | async\n }}</span>\n </div>\n}\n\n@if (ngControl?.hasError('email') && !customErrors['email']) {\n <div class=\"errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ translationContextErrors + 'EMAIL_INCORRECT' | uiTranslate | async }}</span>\n </div>\n}\n\n@if (errorMessage) {\n <div class=\"custom-errors\">\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"'Error'\"></ui-icon>\n <span>{{ errorMessage }}</span>\n </div>\n}\n","queryGuide":{"roles":[],"ariaAttributes":[],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-icon"],"suggestedQueries":[]},"filePath":"components/validation-error/validation-error.component.ts","supportsTheme":true,"lastModified":1786626756423.8484},"write-with-ai":{"name":"WriteWithAiComponent","selector":"ui-write-with-ai","category":"components","description":"An inline AI writing assistant that helps users draft and refine text without leaving the field they're working in. Users describe what they want in a free-text prompt — or pick a preset tag like 'Make it formal' or 'Make it shorter' — and the component generates or rewrites the content for them to accept or refine. Built for content-creation flows such as drafting candidate outreach emails, with built-in idle, loading, success, and error states and a pluggable LLM config (OpenAI, OpenAI-compatible, or Gemini).","importPath":"@testgorilla/tgo-ui/components/write-with-ai","moduleName":"WriteWithAiComponentModule","standalone":true,"changeDetection":"OnPush","inputs":[{"name":"value","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"standalone","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"defaultOpen","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"fieldLabel","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"header","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"placeholder","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"promptValue","type":"string","defaultValue":"''","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"tags","type":"WriteWithAiTagOption[]","defaultValue":"[]","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"status","type":"WriteWithAiStatus","defaultValue":"'idle'","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"subheader","type":"string | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"llmConfig","type":"WriteWithAiLlmConfig | undefined","defaultValue":"undefined","required":false,"alias":null,"transform":null,"description":"","isSignal":true},{"name":"fullHeight","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Layout: make the inner field stretch to fill its container height.","isSignal":true},{"name":"borderless","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"Layout: render the inner field without a visible border.","isSignal":true},{"name":"tagsOnly","type":"boolean","defaultValue":"false","required":false,"alias":null,"transform":null,"description":"When true, hides the free-text `ui-prompt` and renders the `tags` chips on their own.\nClicking a chip submits its label as the prompt via `promptSubmit`.\nUse this when the host backend only accepts a constrained set of prompts (e.g. an enum).","isSignal":true},{"name":"metadata","type":"Record<string, unknown>","defaultValue":"{}","required":false,"alias":null,"transform":null,"description":"Opaque host metadata emitted with promptSubmit for backend integrations.","isSignal":true}],"outputs":[{"name":"valueChange","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"promptChange","eventType":"string","alias":null,"description":"","isSignal":true},{"name":"promptSubmit","eventType":"WriteWithAiSubmitEvent","alias":null,"description":"","isSignal":true},{"name":"cancelPending","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"acceptResult","eventType":"void","alias":null,"description":"","isSignal":true},{"name":"requestRefine","eventType":"void","alias":null,"description":"","isSignal":true}],"module":{"name":"WriteWithAiComponentModule","imports":["WriteWithAiComponent"],"exports":["WriteWithAiComponent"],"declarations":[],"providers":[]},"types":{"typeAliases":[{"name":"WriteWithAiStatus","value":"'idle' | 'loading' | 'success' | 'error'"}],"interfaces":[{"name":"WriteWithAiTagOption","properties":[{"name":"id","type":"number","optional":false},{"name":"label","type":"string","optional":false},{"name":"description","type":"string","optional":true}]},{"name":"WriteWithAiLlmConfig","properties":[{"name":"default","type":"boolean","optional":true},{"name":"context","type":"string","optional":true},{"name":"apiKey","type":"string","optional":true},{"name":"model","type":"string","optional":true},{"name":"provider","type":"'openai' | 'openai-compatible' | 'gemini'","optional":true},{"name":"endpoint","type":"string","optional":true},{"name":"baseUrl","type":"string","optional":true}]},{"name":"WriteWithAiSubmitEvent","properties":[{"name":"prompt","type":"string","optional":false},{"name":"content","type":"string","optional":false},{"name":"metadata","type":"Record<string, unknown>","optional":false}]}],"enums":[]},"examples":[],"documentation":"An inline AI writing assistant that helps users draft and refine text without leaving the field they're working in. Users describe what they want in a free-text prompt — or pick a preset tag like 'Make it formal' or 'Make it shorter' — and the component generates or rewrites the content for them to accept or refine. Built for content-creation flows such as drafting candidate outreach emails, with built-in idle, loading, success, and error states and a pluggable LLM config (OpenAI, OpenAI-compatible, or Gemini).\n\n## When to use\n\n- **Drafting or refining text in place** — when users benefit from AI help writing or rewriting content like emails, messages, or descriptions without leaving the field\n- **One-click refinements** — offer preset tags ('Make it formal', 'Make it shorter') alongside or instead of a free-text prompt; use tagsOnly to show the chips on their own\n- **Embedded or standalone** — embedded (the default) hosts its own textarea with the AI affordance inside the field's frame (use it in place of a bare textarea, never as a button below one), or run it standalone as a self-contained writing surface\n\n## When NOT to use\n\n- **Short or structured input** → use a Field, Dropdown, or Autocomplete; AI generation adds friction where a simple control is clearer\n- **Unreviewed AI output** → pair the generated text with the AI Caveat and keep a human in the loop before it's sent or published\n- **No working LLM behind it** → don't surface the prompt without a configured llmConfig (or a simulation fallback); a prompt that can't generate erodes trust\n","whenToUse":["**Drafting or refining text in place** — when users benefit from AI help writing or rewriting content like emails, messages, or descriptions without leaving the field","**One-click refinements** — offer preset tags ('Make it formal', 'Make it shorter') alongside or instead of a free-text prompt; use tagsOnly to show the chips on their own","**Embedded or standalone** — embedded (the default) hosts its own textarea with the AI affordance inside the field's frame (use it in place of a bare textarea, never as a button below one), or run it standalone as a self-contained writing surface"],"whenNotToUse":["**Short or structured input** → use a Field, Dropdown, or Autocomplete; AI generation adds friction where a simple control is clearer","**Unreviewed AI output** → pair the generated text with the AI Caveat and keep a human in the loop before it's sent or published","**No working LLM behind it** → don't surface the prompt without a configured llmConfig (or a simulation fallback); a prompt that can't generate erodes trust"],"template":"<div\n class=\"write-with-ai\"\n [class.write-with-ai--standalone]=\"standalone()\"\n [class.write-with-ai--full-height]=\"fullHeight()\"\n>\n @if (!standalone()) {\n <div class=\"write-with-ai__host\">\n <ui-field\n [label]=\"fieldLabel() || (translationContext + 'FIELD_LABEL' | uiTranslate | async)!\"\n type=\"textarea\"\n [showBottomContent]=\"false\"\n [fullHeight]=\"fullHeight()\"\n [borderless]=\"borderless()\"\n [disabled]=\"cvaDisabled()\"\n [ngModel]=\"effectiveValue()\"\n (ngModelChange)=\"handleValueInput($event)\"\n ></ui-field>\n <ui-button\n class=\"write-with-ai__cta\"\n variant=\"ghost-ai\"\n size=\"small\"\n [label]=\"(translationContext + 'AI_ASSIST' | uiTranslate | async)!\"\n iconName=\"Sparkle-in-line\"\n iconPosition=\"left\"\n [disabled]=\"isPanelVisible() || cvaDisabled()\"\n (buttonClickEvent)=\"togglePanel()\"\n ></ui-button>\n </div>\n }\n\n @if (isPanelVisible()) {\n <section\n class=\"write-with-ai__panel\"\n [class.write-with-ai__panel--loading]=\"effectiveStatus() === 'loading'\"\n role=\"region\"\n [attr.aria-label]=\"(translationContext + 'AI_WRITING_ASSISTANT' | uiTranslate | async)!\"\n >\n <header class=\"write-with-ai__panel-header\">\n <div class=\"write-with-ai__panel-heading\">\n <div class=\"write-with-ai__title-row\">\n @if (effectiveStatus() !== 'loading') {\n <ui-icon class=\"write-with-ai__title-icon\" name=\"Sparkle-in-line\" color=\"ai\"></ui-icon>\n }\n <h4 class=\"write-with-ai__title\">\n {{ header() || (translationContext + 'HEADER' | uiTranslate | async) }}\n </h4>\n </div>\n @if (subheader()) {\n <p class=\"write-with-ai__subheader\">{{ subheader() }}</p>\n }\n </div>\n @if (!standalone()) {\n <ui-button\n variant=\"icon-button\"\n iconName=\"Close\"\n size=\"small\"\n [tooltip]=\"(translationContext + 'CLOSE' | uiTranslate | async)!\"\n [ariaLabel]=\"(translationContext + 'CLOSE_AI_ASSISTANT' | uiTranslate | async)!\"\n (buttonClickEvent)=\"togglePanel()\"\n ></ui-button>\n }\n </header>\n\n @if (effectiveStatus() === 'idle') {\n @if (tagsOnly()) {\n @if (tags().length) {\n <div class=\"write-with-ai__tags-prompt\">\n <div\n class=\"write-with-ai__tags-container\"\n role=\"group\"\n [attr.aria-label]=\"header() || (translationContext + 'HEADER' | uiTranslate | async)\"\n #tagsScrollContainer\n (scroll)=\"measureTagScroll(tagsScrollContainer)\"\n >\n @for (tag of tags(); track tag.id) {\n <ui-tag\n [label]=\"tag.label\"\n [ariaLabel]=\"tag.label\"\n [isDisabled]=\"cvaDisabled()\"\n (press)=\"handleTagClick(tag)\"\n ></ui-tag>\n }\n </div>\n <div class=\"write-with-ai__tags-arrows\">\n <ui-button\n variant=\"icon-button\"\n size=\"small\"\n iconName=\"Arrow-chevron-left-in-line\"\n [tooltip]=\"(translationContext + 'SCROLL_TAGS_LEFT' | uiTranslate | async)!\"\n [ariaLabel]=\"(translationContext + 'SCROLL_TAGS_LEFT' | uiTranslate | async)!\"\n [disabled]=\"cvaDisabled() || !canScrollTagsLeft()\"\n (buttonClickEvent)=\"scrollTags(tagsScrollContainer, -1)\"\n ></ui-button>\n <ui-button\n variant=\"icon-button\"\n size=\"small\"\n iconName=\"Arrow-chevron-right-in-line\"\n [tooltip]=\"(translationContext + 'SCROLL_TAGS_RIGHT' | uiTranslate | async)!\"\n [ariaLabel]=\"(translationContext + 'SCROLL_TAGS_RIGHT' | uiTranslate | async)!\"\n [disabled]=\"cvaDisabled() || !canScrollTagsRight()\"\n (buttonClickEvent)=\"scrollTags(tagsScrollContainer, 1)\"\n ></ui-button>\n </div>\n </div>\n }\n } @else {\n <div class=\"write-with-ai__prompt\">\n <ui-prompt\n [ngModel]=\"promptModelData()\"\n (ngModelChange)=\"handlePromptModelChange($event)\"\n [tags]=\"promptTags()\"\n [placeholder]=\"placeholder() || (translationContext + 'PLACEHOLDER' | uiTranslate | async)!\"\n [showSendButton]=\"true\"\n (promptData)=\"handlePromptSubmit($event)\"\n ></ui-prompt>\n </div>\n }\n }\n\n @if (statusConfig(); as cfg) {\n <ui-alert-banner\n [alertType]=\"cfg.alertType\"\n alertVariant=\"callout\"\n [message]=\"(cfg.messageKey | uiTranslate | async)!\"\n [includeDismissButton]=\"false\"\n [isLoading]=\"cfg.variant === 'loading'\"\n [hasIcon]=\"cfg.variant !== 'loading'\"\n [actions]=\"cfg.actions\"\n [isFullWidth]=\"true\"\n ></ui-alert-banner>\n }\n\n <div class=\"write-with-ai__caveat\">\n <ui-ai-caveat></ui-ai-caveat>\n </div>\n </section>\n }\n</div>\n","queryGuide":{"roles":["region","group"],"ariaAttributes":["aria-label"],"dataTestIds":[],"materialComponents":[],"childUIComponents":["ui-field","ui-button","ui-icon","ui-tag","ui-prompt","ui-alert-banner","ui-ai-caveat"],"suggestedQueries":["screen.getByRole('region')","screen.getByRole('group')","screen.getByLabelText('label text')"]},"filePath":"components/write-with-ai/write-with-ai.component.ts","supportsTheme":false,"designUrl":"https://www.figma.com/design/FwPmwOHXkG8SqzPCqxiK23/%F0%9F%8C%B3-Canopy-UI?node-id=21472-62578","lastModified":1786626756424.6055}},"icons":[{"name":"Add","svgPath":"assets/icons/Add.svg"},{"name":"Add-filled","svgPath":"assets/icons/rebrand/Add-filled.svg"},{"name":"Add-in-line","svgPath":"assets/icons/rebrand/Add-in-line.svg"},{"name":"Ai-fluent-filled","svgPath":"assets/icons/rebrand/Ai-fluent-filled.svg"},{"name":"Ai-fluent-in-line","svgPath":"assets/icons/rebrand/Ai-fluent-in-line.svg"},{"name":"Analytics-filled","svgPath":"assets/icons/rebrand/Analytics-filled.svg"},{"name":"Analytics-filled-AI","svgPath":"assets/icons/rebrand/Analytics-filled-AI.svg"},{"name":"Analytics-in-line","svgPath":"assets/icons/rebrand/Analytics-in-line.svg"},{"name":"Analytics-in-line-AI","svgPath":"assets/icons/rebrand/Analytics-in-line-AI.svg"},{"name":"Analytics-v2-filled","svgPath":"assets/icons/rebrand/Analytics-v2-filled.svg"},{"name":"Analytics-v2-in-line","svgPath":"assets/icons/rebrand/Analytics-v2-in-line.svg"},{"name":"Applications-in-line","svgPath":"assets/icons/rebrand/Applications-in-line.svg"},{"name":"Archive","svgPath":"assets/icons/Archive.svg"},{"name":"Archive-filled","svgPath":"assets/icons/rebrand/Archive-filled.svg"},{"name":"Archive-in-line","svgPath":"assets/icons/rebrand/Archive-in-line.svg"},{"name":"Arrow_down","svgPath":"assets/icons/Arrow_down.svg"},{"name":"Arrow_left","svgPath":"assets/icons/Arrow_left.svg"},{"name":"Arrow_right","svgPath":"assets/icons/Arrow_right.svg"},{"name":"Arrow_up","svgPath":"assets/icons/Arrow_up.svg"},{"name":"Arrow-chevron-down-filled","svgPath":"assets/icons/rebrand/Arrow-chevron-down-filled.svg"},{"name":"Arrow-chevron-down-in-line","svgPath":"assets/icons/rebrand/Arrow-chevron-down-in-line.svg"},{"name":"Arrow-chevron-left-filled","svgPath":"assets/icons/rebrand/Arrow-chevron-left-filled.svg"},{"name":"Arrow-chevron-left-in-line","svgPath":"assets/icons/rebrand/Arrow-chevron-left-in-line.svg"},{"name":"Arrow-chevron-right-filled","svgPath":"assets/icons/rebrand/Arrow-chevron-right-filled.svg"},{"name":"Arrow-chevron-right-in-line","svgPath":"assets/icons/rebrand/Arrow-chevron-right-in-line.svg"},{"name":"Arrow-chevron-up-filled","svgPath":"assets/icons/rebrand/Arrow-chevron-up-filled.svg"},{"name":"Arrow-chevron-up-in-line","svgPath":"assets/icons/rebrand/Arrow-chevron-up-in-line.svg"},{"name":"Arrow-down-filled","svgPath":"assets/icons/rebrand/Arrow-down-filled.svg"},{"name":"Arrow-down-in-line","svgPath":"assets/icons/rebrand/Arrow-down-in-line.svg"},{"name":"Arrow-up-filled","svgPath":"assets/icons/rebrand/Arrow-up-filled.svg"},{"name":"Arrow-up-in-line","svgPath":"assets/icons/rebrand/Arrow-up-in-line.svg"},{"name":"Assessment","svgPath":"assets/icons/Assessment.svg"},{"name":"Assessment-filled","svgPath":"assets/icons/rebrand/Assessment-filled.svg"},{"name":"Assessment-in-line","svgPath":"assets/icons/rebrand/Assessment-in-line.svg"},{"name":"Attach-filled","svgPath":"assets/icons/rebrand/Attach-filled.svg"},{"name":"Attach-in-line","svgPath":"assets/icons/rebrand/Attach-in-line.svg"},{"name":"Attempts","svgPath":"assets/icons/Attempts.svg"},{"name":"Attempts-filled","svgPath":"assets/icons/rebrand/Attempts-filled.svg"},{"name":"Attempts-in-line","svgPath":"assets/icons/rebrand/Attempts-in-line.svg"},{"name":"Book","svgPath":"assets/icons/Book.svg"},{"name":"Book-filled","svgPath":"assets/icons/rebrand/Book-filled.svg"},{"name":"Book-in-line","svgPath":"assets/icons/rebrand/Book-in-line.svg"},{"name":"Bookmark-filled","svgPath":"assets/icons/rebrand/Bookmark-filled.svg"},{"name":"Bookmark-in-line","svgPath":"assets/icons/rebrand/Bookmark-in-line.svg"},{"name":"Calendar","svgPath":"assets/icons/Calendar.svg"},{"name":"Calendar-filled","svgPath":"assets/icons/rebrand/Calendar-filled.svg"},{"name":"Calendar-in-line","svgPath":"assets/icons/rebrand/Calendar-in-line.svg"},{"name":"Campaign-filled","svgPath":"assets/icons/rebrand/Campaign-filled.svg"},{"name":"Campaign-in-line","svgPath":"assets/icons/rebrand/Campaign-in-line.svg"},{"name":"Candidates","svgPath":"assets/icons/Candidates.svg"},{"name":"Candidates-filled","svgPath":"assets/icons/rebrand/Candidates-filled.svg"},{"name":"Candidates-in-line","svgPath":"assets/icons/rebrand/Candidates-in-line.svg"},{"name":"Candidates-v2-filled","svgPath":"assets/icons/rebrand/Candidates-v2-filled.svg"},{"name":"Candidates-v2-in-line","svgPath":"assets/icons/rebrand/Candidates-v2-in-line.svg"},{"name":"Chat","svgPath":"assets/icons/Chat.svg"},{"name":"Chat-filled","svgPath":"assets/icons/rebrand/Chat-filled.svg"},{"name":"Chat-filled-AI","svgPath":"assets/icons/rebrand/Chat-filled-AI.svg"},{"name":"Chat-in-line","svgPath":"assets/icons/rebrand/Chat-in-line.svg"},{"name":"Chat-in-line-AI","svgPath":"assets/icons/rebrand/Chat-in-line-AI.svg"},{"name":"Chat-notifications","svgPath":"assets/icons/Chat-notifications.svg"},{"name":"Check","svgPath":"assets/icons/Check.svg"},{"name":"Check-filled","svgPath":"assets/icons/rebrand/Check-filled.svg"},{"name":"Check-in-line","svgPath":"assets/icons/rebrand/Check-in-line.svg"},{"name":"Check-round-filled","svgPath":"assets/icons/rebrand/Check-round-filled.svg"},{"name":"Check-round-in-line","svgPath":"assets/icons/rebrand/Check-round-in-line.svg"},{"name":"Check-small-filled","svgPath":"assets/icons/rebrand/Check-small-filled.svg"},{"name":"Check-small-in-line","svgPath":"assets/icons/rebrand/Check-small-in-line.svg"},{"name":"Circle-filled","svgPath":"assets/icons/rebrand/Circle-filled.svg"},{"name":"Circle-in-line","svgPath":"assets/icons/rebrand/Circle-in-line.svg"},{"name":"Clone","svgPath":"assets/icons/Clone.svg"},{"name":"Clone-filled","svgPath":"assets/icons/rebrand/Clone-filled.svg"},{"name":"Clone-in-line","svgPath":"assets/icons/rebrand/Clone-in-line.svg"},{"name":"Close","svgPath":"assets/icons/Close.svg"},{"name":"Close-filled","svgPath":"assets/icons/rebrand/Close-filled.svg"},{"name":"Close-in-line","svgPath":"assets/icons/rebrand/Close-in-line.svg"},{"name":"Close-round-filled","svgPath":"assets/icons/rebrand/Close-round-filled.svg"},{"name":"Close-round-in-line","svgPath":"assets/icons/rebrand/Close-round-in-line.svg"},{"name":"Code","svgPath":"assets/icons/Code.svg"},{"name":"Code-filled","svgPath":"assets/icons/rebrand/Code-filled.svg"},{"name":"Code-in-line","svgPath":"assets/icons/rebrand/Code-in-line.svg"},{"name":"Company","svgPath":"assets/icons/Company.svg"},{"name":"Company-filled","svgPath":"assets/icons/rebrand/Company-filled.svg"},{"name":"Company-in-line","svgPath":"assets/icons/rebrand/Company-in-line.svg"},{"name":"Copy","svgPath":"assets/icons/Copy.svg"},{"name":"Copy-filled","svgPath":"assets/icons/rebrand/Copy-filled.svg"},{"name":"Copy-in-line","svgPath":"assets/icons/rebrand/Copy-in-line.svg"},{"name":"Corporate-filled","svgPath":"assets/icons/rebrand/Corporate-filled.svg"},{"name":"Corporate-in-line","svgPath":"assets/icons/rebrand/Corporate-in-line.svg"},{"name":"Count-one-filled","svgPath":"assets/icons/rebrand/Count-one-filled.svg"},{"name":"Count-one-in-line","svgPath":"assets/icons/rebrand/Count-one-in-line.svg"},{"name":"Count-three-filled","svgPath":"assets/icons/rebrand/Count-three-filled.svg"},{"name":"Count-three-in-line","svgPath":"assets/icons/rebrand/Count-three-in-line.svg"},{"name":"Count-two-filled","svgPath":"assets/icons/rebrand/Count-two-filled.svg"},{"name":"Count-two-in-line","svgPath":"assets/icons/rebrand/Count-two-in-line.svg"},{"name":"Credit-card-filled","svgPath":"assets/icons/rebrand/Credit-card-filled.svg"},{"name":"Credit-card-in-line","svgPath":"assets/icons/rebrand/Credit-card-in-line.svg"},{"name":"Credit-filled","svgPath":"assets/icons/rebrand/Credit-filled.svg"},{"name":"Credit-in-line","svgPath":"assets/icons/rebrand/Credit-in-line.svg"},{"name":"Custom-questions","svgPath":"assets/icons/Custom-questions.svg"},{"name":"Delete","svgPath":"assets/icons/Delete.svg"},{"name":"Delete-filled","svgPath":"assets/icons/rebrand/Delete-filled.svg"},{"name":"Delete-in-line","svgPath":"assets/icons/rebrand/Delete-in-line.svg"},{"name":"Demo-filled","svgPath":"assets/icons/rebrand/Demo-filled.svg"},{"name":"Demo-in-line","svgPath":"assets/icons/rebrand/Demo-in-line.svg"},{"name":"Device-rotate-in-line","svgPath":"assets/icons/rebrand/Device-rotate-in-line.svg"},{"name":"Devices","svgPath":"assets/icons/Devices.svg"},{"name":"Devices-filled","svgPath":"assets/icons/rebrand/Devices-filled.svg"},{"name":"Devices-in-line","svgPath":"assets/icons/rebrand/Devices-in-line.svg"},{"name":"Disconnected-in-line","svgPath":"assets/icons/rebrand/Disconnected-in-line.svg"},{"name":"Document","svgPath":"assets/icons/Document.svg"},{"name":"Document-filled","svgPath":"assets/icons/rebrand/Document-filled.svg"},{"name":"Document-filled-AI","svgPath":"assets/icons/rebrand/Document-filled-AI.svg"},{"name":"Document-in-line","svgPath":"assets/icons/rebrand/Document-in-line.svg"},{"name":"Document-in-line-AI","svgPath":"assets/icons/rebrand/Document-in-line-AI.svg"},{"name":"Dot","svgPath":"assets/icons/Dot.svg"},{"name":"Dot-filled","svgPath":"assets/icons/rebrand/Dot-filled.svg"},{"name":"Dot-in-line","svgPath":"assets/icons/rebrand/Dot-in-line.svg"},{"name":"Download","svgPath":"assets/icons/Download.svg"},{"name":"Download-filled","svgPath":"assets/icons/rebrand/Download-filled.svg"},{"name":"Download-in-line","svgPath":"assets/icons/rebrand/Download-in-line.svg"},{"name":"Edit","svgPath":"assets/icons/Edit.svg"},{"name":"Edit-filled","svgPath":"assets/icons/rebrand/Edit-filled.svg"},{"name":"Edit-filled-AI","svgPath":"assets/icons/rebrand/Edit-filled-AI.svg"},{"name":"Edit-in-line","svgPath":"assets/icons/rebrand/Edit-in-line.svg"},{"name":"Edit-in-line-AI","svgPath":"assets/icons/rebrand/Edit-in-line-AI.svg"},{"name":"Edit-text-filled","svgPath":"assets/icons/rebrand/Edit-text-filled.svg"},{"name":"Edit-text-in-line","svgPath":"assets/icons/rebrand/Edit-text-in-line.svg"},{"name":"Email-message","svgPath":"assets/icons/Email-message.svg"},{"name":"Email-unsubscribed-filled","svgPath":"assets/icons/rebrand/Email-unsubscribed-filled.svg"},{"name":"Email-unsubscribed-in-line","svgPath":"assets/icons/rebrand/Email-unsubscribed-in-line.svg"},{"name":"Employee","svgPath":"assets/icons/Employee.svg"},{"name":"Employee-filled","svgPath":"assets/icons/rebrand/Employee-filled.svg"},{"name":"Employee-in-line","svgPath":"assets/icons/rebrand/Employee-in-line.svg"},{"name":"Empty-placeholder","svgPath":"assets/icons/Empty-placeholder.svg"},{"name":"Empty-search-in-line","svgPath":"assets/icons/rebrand/Empty-search-in-line.svg"},{"name":"Error","svgPath":"assets/icons/Error.svg"},{"name":"Error-filled","svgPath":"assets/icons/rebrand/Error-filled.svg"},{"name":"Error-in-line","svgPath":"assets/icons/rebrand/Error-in-line.svg"},{"name":"Essay","svgPath":"assets/icons/Essay.svg"},{"name":"Essay-filled","svgPath":"assets/icons/rebrand/Essay-filled.svg"},{"name":"Essay-in-line","svgPath":"assets/icons/rebrand/Essay-in-line.svg"},{"name":"Experience-filled","svgPath":"assets/icons/rebrand/Experience-filled.svg"},{"name":"Experience-in-line","svgPath":"assets/icons/rebrand/Experience-in-line.svg"},{"name":"Eye-hide","svgPath":"assets/icons/Eye-hide.svg"},{"name":"Eye-view","svgPath":"assets/icons/Eye-view.svg"},{"name":"Facebook-filled","svgPath":"assets/icons/rebrand/Facebook-filled.svg"},{"name":"Facebook-in-line","svgPath":"assets/icons/rebrand/Facebook-in-line.svg"},{"name":"Failed-to-load-filled","svgPath":"assets/icons/rebrand/Failed-to-load-filled.svg"},{"name":"Failed-to-load-in-line","svgPath":"assets/icons/rebrand/Failed-to-load-in-line.svg"},{"name":"Fast-forward","svgPath":"assets/icons/Fast-forward.svg"},{"name":"Fast-forward-filled","svgPath":"assets/icons/rebrand/Fast-forward-filled.svg"},{"name":"Fast-forward-in-line","svgPath":"assets/icons/rebrand/Fast-forward-in-line.svg"},{"name":"Fast-rewind","svgPath":"assets/icons/Fast-rewind.svg"},{"name":"Fast-rewind-filled","svgPath":"assets/icons/rebrand/Fast-rewind-filled.svg"},{"name":"Fast-rewind-in-line","svgPath":"assets/icons/rebrand/Fast-rewind-in-line.svg"},{"name":"Feedback-1","svgPath":"assets/icons/Feedback-1.svg"},{"name":"Feedback-2","svgPath":"assets/icons/Feedback-2.svg"},{"name":"Feedback-3","svgPath":"assets/icons/Feedback-3.svg"},{"name":"Feedback-4","svgPath":"assets/icons/Feedback-4.svg"},{"name":"Feedback-5","svgPath":"assets/icons/Feedback-5.svg"},{"name":"File-attach","svgPath":"assets/icons/File-attach.svg"},{"name":"File-upload","svgPath":"assets/icons/File-upload.svg"},{"name":"Filter","svgPath":"assets/icons/Filter.svg"},{"name":"Filter-filled","svgPath":"assets/icons/rebrand/Filter-filled.svg"},{"name":"Filter-in-line","svgPath":"assets/icons/rebrand/Filter-in-line.svg"},{"name":"Fire","svgPath":"assets/icons/Fire.svg"},{"name":"Fire-filled","svgPath":"assets/icons/rebrand/Fire-filled.svg"},{"name":"Fire-in-line","svgPath":"assets/icons/rebrand/Fire-in-line.svg"},{"name":"Flag-filled","svgPath":"assets/icons/rebrand/Flag-filled.svg"},{"name":"Flag-in-line","svgPath":"assets/icons/rebrand/Flag-in-line.svg"},{"name":"Folder","svgPath":"assets/icons/Folder.svg"},{"name":"Folder-filled","svgPath":"assets/icons/rebrand/Folder-filled.svg"},{"name":"Folder-in-line","svgPath":"assets/icons/rebrand/Folder-in-line.svg"},{"name":"Format-add-file","svgPath":"assets/icons/Format-add-file.svg"},{"name":"Format-add-table","svgPath":"assets/icons/Format-add-table.svg"},{"name":"Format-align-center","svgPath":"assets/icons/Format-align-center.svg"},{"name":"Format-align-justify","svgPath":"assets/icons/Format-align-justify.svg"},{"name":"Format-align-L","svgPath":"assets/icons/Format-align-L.svg"},{"name":"Format-align-R","svgPath":"assets/icons/Format-align-R.svg"},{"name":"Format-bold","svgPath":"assets/icons/Format-bold.svg"},{"name":"Format-code-active","svgPath":"assets/icons/Format-code-active.svg"},{"name":"Format-code-block","svgPath":"assets/icons/Format-code-block.svg"},{"name":"Format-edit-table","svgPath":"assets/icons/Format-edit-table.svg"},{"name":"Format-format","svgPath":"assets/icons/Format-format.svg"},{"name":"Format-function","svgPath":"assets/icons/Format-function.svg"},{"name":"Format-italics","svgPath":"assets/icons/Format-italics.svg"},{"name":"Format-list-bulleted","svgPath":"assets/icons/Format-list-bulleted.svg"},{"name":"Format-list-numbered","svgPath":"assets/icons/Format-list-numbered.svg"},{"name":"Format-picker","svgPath":"assets/icons/Format-picker.svg"},{"name":"Format-quote","svgPath":"assets/icons/Format-quote.svg"},{"name":"Format-subscript","svgPath":"assets/icons/Format-subscript.svg"},{"name":"Format-superscript","svgPath":"assets/icons/Format-superscript.svg"},{"name":"Format-text-direction-L","svgPath":"assets/icons/Format-text-direction-L.svg"},{"name":"Format-text-direction-R","svgPath":"assets/icons/Format-text-direction-R.svg"},{"name":"Format-text-size","svgPath":"assets/icons/Format-text-size.svg"},{"name":"Format-underline","svgPath":"assets/icons/Format-underline.svg"},{"name":"Format-variable","svgPath":"assets/icons/Format-variable.svg"},{"name":"Full-screen","svgPath":"assets/icons/Full-screen.svg"},{"name":"Full-screen-filled","svgPath":"assets/icons/rebrand/Full-screen-filled.svg"},{"name":"Full-screen-in-line","svgPath":"assets/icons/rebrand/Full-screen-in-line.svg"},{"name":"Gender-female","svgPath":"assets/icons/Gender-female.svg"},{"name":"Gender-female-filled","svgPath":"assets/icons/rebrand/Gender-female-filled.svg"},{"name":"Gender-female-in-line","svgPath":"assets/icons/rebrand/Gender-female-in-line.svg"},{"name":"Gender-male","svgPath":"assets/icons/Gender-male.svg"},{"name":"Gender-male-filled","svgPath":"assets/icons/rebrand/Gender-male-filled.svg"},{"name":"Gender-male-in-line","svgPath":"assets/icons/rebrand/Gender-male-in-line.svg"},{"name":"Gift","svgPath":"assets/icons/Gift.svg"},{"name":"Gift-filled","svgPath":"assets/icons/rebrand/Gift-filled.svg"},{"name":"Gift-in-line","svgPath":"assets/icons/rebrand/Gift-in-line.svg"},{"name":"Google","svgPath":"assets/icons/Google.svg"},{"name":"Google-filled","svgPath":"assets/icons/rebrand/Google-filled.svg"},{"name":"Google-in-line","svgPath":"assets/icons/rebrand/Google-in-line.svg"},{"name":"Grab","svgPath":"assets/icons/Grab.svg"},{"name":"Grab-filled","svgPath":"assets/icons/rebrand/Grab-filled.svg"},{"name":"Grab-in-line","svgPath":"assets/icons/rebrand/Grab-in-line.svg"},{"name":"Grid-list-view-filled","svgPath":"assets/icons/rebrand/Grid-list-view-filled.svg"},{"name":"Grid-list-view-in-line","svgPath":"assets/icons/rebrand/Grid-list-view-in-line.svg"},{"name":"Help","svgPath":"assets/icons/Help.svg"},{"name":"Help-2","svgPath":"assets/icons/Help-2.svg"},{"name":"Help-center-filled","svgPath":"assets/icons/rebrand/Help-center-filled.svg"},{"name":"Help-center-in-line","svgPath":"assets/icons/rebrand/Help-center-in-line.svg"},{"name":"Help-filled","svgPath":"assets/icons/rebrand/Help-filled.svg"},{"name":"Help-in-line","svgPath":"assets/icons/rebrand/Help-in-line.svg"},{"name":"Hide-filled","svgPath":"assets/icons/rebrand/Hide-filled.svg"},{"name":"Hide-in-line","svgPath":"assets/icons/rebrand/Hide-in-line.svg"},{"name":"History-filled","svgPath":"assets/icons/rebrand/History-filled.svg"},{"name":"History-in-line","svgPath":"assets/icons/rebrand/History-in-line.svg"},{"name":"Home-filled","svgPath":"assets/icons/rebrand/Home-filled.svg"},{"name":"Home-in-line","svgPath":"assets/icons/rebrand/Home-in-line.svg"},{"name":"ID-failed-filled","svgPath":"assets/icons/rebrand/ID-failed-filled.svg"},{"name":"ID-failed-in-line","svgPath":"assets/icons/rebrand/ID-failed-in-line.svg"},{"name":"ID-not-verified-filled","svgPath":"assets/icons/rebrand/ID-not-verified-filled.svg"},{"name":"ID-not-verified-in-line","svgPath":"assets/icons/rebrand/ID-not-verified-in-line.svg"},{"name":"ID-verified-filled","svgPath":"assets/icons/rebrand/ID-verified-filled.svg"},{"name":"ID-verified-in-line","svgPath":"assets/icons/rebrand/ID-verified-in-line.svg"},{"name":"Image","svgPath":"assets/icons/Image.svg"},{"name":"Image-filled","svgPath":"assets/icons/rebrand/Image-filled.svg"},{"name":"Image-in-line","svgPath":"assets/icons/rebrand/Image-in-line.svg"},{"name":"Info","svgPath":"assets/icons/Info.svg"},{"name":"Info-filled","svgPath":"assets/icons/rebrand/Info-filled.svg"},{"name":"Info-in-line","svgPath":"assets/icons/rebrand/Info-in-line.svg"},{"name":"Instagram-filled","svgPath":"assets/icons/rebrand/Instagram-filled.svg"},{"name":"Instagram-in-line","svgPath":"assets/icons/rebrand/Instagram-in-line.svg"},{"name":"Integration","svgPath":"assets/icons/Integration.svg"},{"name":"Integration-filled","svgPath":"assets/icons/rebrand/Integration-filled.svg"},{"name":"Integration-in-line","svgPath":"assets/icons/rebrand/Integration-in-line.svg"},{"name":"Invite-filled","svgPath":"assets/icons/rebrand/Invite-filled.svg"},{"name":"Invite-in-line","svgPath":"assets/icons/rebrand/Invite-in-line.svg"},{"name":"Invoice-filled","svgPath":"assets/icons/rebrand/Invoice-filled.svg"},{"name":"Invoice-in-line","svgPath":"assets/icons/rebrand/Invoice-in-line.svg"},{"name":"Jobs-filled","svgPath":"assets/icons/rebrand/Jobs-filled.svg"},{"name":"Jobs-in-line","svgPath":"assets/icons/rebrand/Jobs-in-line.svg"},{"name":"Language","svgPath":"assets/icons/Language.svg"},{"name":"Language-filled","svgPath":"assets/icons/rebrand/Language-filled.svg"},{"name":"Language-in-line","svgPath":"assets/icons/rebrand/Language-in-line.svg"},{"name":"Layout","svgPath":"assets/icons/Layout.svg"},{"name":"Layout-filled","svgPath":"assets/icons/rebrand/Layout-filled.svg"},{"name":"Layout-in-line","svgPath":"assets/icons/rebrand/Layout-in-line.svg"},{"name":"Learn","svgPath":"assets/icons/Learn.svg"},{"name":"Learn-filled","svgPath":"assets/icons/rebrand/Learn-filled.svg"},{"name":"Learn-in-line","svgPath":"assets/icons/rebrand/Learn-in-line.svg"},{"name":"Level","svgPath":"assets/icons/Level.svg"},{"name":"Level-filled","svgPath":"assets/icons/rebrand/Level-filled.svg"},{"name":"Level-in-line","svgPath":"assets/icons/rebrand/Level-in-line.svg"},{"name":"Library-filled","svgPath":"assets/icons/rebrand/Library-filled.svg"},{"name":"Library-in-line","svgPath":"assets/icons/rebrand/Library-in-line.svg"},{"name":"Light-bulb","svgPath":"assets/icons/Light-bulb.svg"},{"name":"Light-bulb-filled","svgPath":"assets/icons/rebrand/Light-bulb-filled.svg"},{"name":"Light-bulb-in-line","svgPath":"assets/icons/rebrand/Light-bulb-in-line.svg"},{"name":"Link","svgPath":"assets/icons/Link.svg"},{"name":"Link-filled","svgPath":"assets/icons/rebrand/Link-filled.svg"},{"name":"Link-in-line","svgPath":"assets/icons/rebrand/Link-in-line.svg"},{"name":"Linkedin-brand-filled","svgPath":"assets/icons/rebrand/Linkedin-brand-filled.svg"},{"name":"Linkedin-brand-in-line","svgPath":"assets/icons/rebrand/Linkedin-brand-in-line.svg"},{"name":"Linkedin-filled","svgPath":"assets/icons/rebrand/Linkedin-filled.svg"},{"name":"Linkedin-in-line","svgPath":"assets/icons/rebrand/Linkedin-in-line.svg"},{"name":"Loading-spinner","svgPath":"assets/icons/Loading-spinner.svg"},{"name":"Localisation","svgPath":"assets/icons/Localisation.svg"},{"name":"Localization-filled","svgPath":"assets/icons/rebrand/Localization-filled.svg"},{"name":"Localization-in-line","svgPath":"assets/icons/rebrand/Localization-in-line.svg"},{"name":"Location","svgPath":"assets/icons/Location.svg"},{"name":"Location-filled","svgPath":"assets/icons/rebrand/Location-filled.svg"},{"name":"Location-in-line","svgPath":"assets/icons/rebrand/Location-in-line.svg"},{"name":"Lock","svgPath":"assets/icons/Lock.svg"},{"name":"Lock-filled","svgPath":"assets/icons/rebrand/Lock-filled.svg"},{"name":"Lock-in-line","svgPath":"assets/icons/rebrand/Lock-in-line.svg"},{"name":"Log-out","svgPath":"assets/icons/Log-out.svg"},{"name":"Logout-filled","svgPath":"assets/icons/rebrand/Logout-filled.svg"},{"name":"Logout-in-line","svgPath":"assets/icons/rebrand/Logout-in-line.svg"},{"name":"Lost-found-404-in-line","svgPath":"assets/icons/rebrand/Lost-found-404-in-line.svg"},{"name":"Medal-filled","svgPath":"assets/icons/rebrand/Medal-filled.svg"},{"name":"Medal-in-line","svgPath":"assets/icons/rebrand/Medal-in-line.svg"},{"name":"Menu-burger","svgPath":"assets/icons/Menu-burger.svg"},{"name":"Menu-burger-filled","svgPath":"assets/icons/rebrand/Menu-burger-filled.svg"},{"name":"Menu-burger-in-line","svgPath":"assets/icons/rebrand/Menu-burger-in-line.svg"},{"name":"Menu-close","svgPath":"assets/icons/Menu-close.svg"},{"name":"Menu-close-filled","svgPath":"assets/icons/rebrand/Menu-close-filled.svg"},{"name":"Menu-close-in-line","svgPath":"assets/icons/rebrand/Menu-close-in-line.svg"},{"name":"Menu-ellipsis","svgPath":"assets/icons/Menu-ellipsis.svg"},{"name":"Menu-ellipsis-filled","svgPath":"assets/icons/rebrand/Menu-ellipsis-filled.svg"},{"name":"Menu-ellipsis-in-line","svgPath":"assets/icons/rebrand/Menu-ellipsis-in-line.svg"},{"name":"Menu-open","svgPath":"assets/icons/Menu-open.svg"},{"name":"Menu-open-filled","svgPath":"assets/icons/rebrand/Menu-open-filled.svg"},{"name":"Menu-open-in-line","svgPath":"assets/icons/rebrand/Menu-open-in-line.svg"},{"name":"Messages-in-line","svgPath":"assets/icons/rebrand/Messages-in-line.svg"},{"name":"Mic-filled","svgPath":"assets/icons/rebrand/Mic-filled.svg"},{"name":"Mic-filled-AI","svgPath":"assets/icons/rebrand/Mic-filled-AI.svg"},{"name":"Mic-in-line","svgPath":"assets/icons/rebrand/Mic-in-line.svg"},{"name":"Mic-in-line-AI","svgPath":"assets/icons/rebrand/Mic-in-line-AI.svg"},{"name":"Mic-off-filled","svgPath":"assets/icons/rebrand/Mic-off-filled.svg"},{"name":"Mic-off-in-line","svgPath":"assets/icons/rebrand/Mic-off-in-line.svg"},{"name":"Microphone","svgPath":"assets/icons/Microphone.svg"},{"name":"Microsoft","svgPath":"assets/icons/Microsoft.svg"},{"name":"Microsoft-filled","svgPath":"assets/icons/rebrand/Microsoft-filled.svg"},{"name":"Microsoft-in-line","svgPath":"assets/icons/rebrand/Microsoft-in-line.svg"},{"name":"Minimize","svgPath":"assets/icons/Minimize.svg"},{"name":"Minimize-filled","svgPath":"assets/icons/rebrand/Minimize-filled.svg"},{"name":"Minimize-in-line","svgPath":"assets/icons/rebrand/Minimize-in-line.svg"},{"name":"Minus","svgPath":"assets/icons/Minus.svg"},{"name":"Minus-filled","svgPath":"assets/icons/rebrand/Minus-filled.svg"},{"name":"Minus-in-line","svgPath":"assets/icons/rebrand/Minus-in-line.svg"},{"name":"Money-bag","svgPath":"assets/icons/Money-bag.svg"},{"name":"Mouse","svgPath":"assets/icons/Mouse.svg"},{"name":"Mouse-cursor","svgPath":"assets/icons/Mouse-cursor.svg"},{"name":"Mouse-filled","svgPath":"assets/icons/rebrand/Mouse-filled.svg"},{"name":"Mouse-grab-cursor","svgPath":"assets/icons/Mouse-grab-cursor.svg"},{"name":"Mouse-in-line","svgPath":"assets/icons/rebrand/Mouse-in-line.svg"},{"name":"Multi-choice","svgPath":"assets/icons/Multi-choice.svg"},{"name":"Multi-choice-filled","svgPath":"assets/icons/rebrand/Multi-choice-filled.svg"},{"name":"Multi-choice-in-line","svgPath":"assets/icons/rebrand/Multi-choice-in-line.svg"},{"name":"Notification-bell-filled","svgPath":"assets/icons/rebrand/Notification-bell-filled.svg"},{"name":"Notification-bell-in-line","svgPath":"assets/icons/rebrand/Notification-bell-in-line.svg"},{"name":"Notification-bell-paused-in-line","svgPath":"assets/icons/rebrand/Notification-bell-paused-in-line.svg"},{"name":"Notifications","svgPath":"assets/icons/Notifications.svg"},{"name":"Open-in-new-filled","svgPath":"assets/icons/rebrand/Open-in-new-filled.svg"},{"name":"Open-in-new-in-line","svgPath":"assets/icons/rebrand/Open-in-new-in-line.svg"},{"name":"Open-lock-filled","svgPath":"assets/icons/rebrand/Open-lock-filled.svg"},{"name":"Open-lock-in-line","svgPath":"assets/icons/rebrand/Open-lock-in-line.svg"},{"name":"Password","svgPath":"assets/icons/Password.svg"},{"name":"Password-filled","svgPath":"assets/icons/rebrand/Password-filled.svg"},{"name":"Password-in-line","svgPath":"assets/icons/rebrand/Password-in-line.svg"},{"name":"Path","svgPath":"assets/icons/Path.svg"},{"name":"Path-filled","svgPath":"assets/icons/rebrand/Path-filled.svg"},{"name":"Path-in-line","svgPath":"assets/icons/rebrand/Path-in-line.svg"},{"name":"Pause-filled","svgPath":"assets/icons/rebrand/Pause-filled.svg"},{"name":"Pause-in-line","svgPath":"assets/icons/rebrand/Pause-in-line.svg"},{"name":"Pencil-to-gorilla-filled","svgPath":"assets/icons/rebrand/Pencil-to-gorilla-filled.svg"},{"name":"Pencil-to-gorilla-in-line","svgPath":"assets/icons/rebrand/Pencil-to-gorilla-in-line.svg"},{"name":"Phone-rings-filled","svgPath":"assets/icons/rebrand/Phone-rings-filled.svg"},{"name":"Phone-rings-in-line","svgPath":"assets/icons/rebrand/Phone-rings-in-line.svg"},{"name":"Pin-marker","svgPath":"assets/icons/Pin-marker.svg"},{"name":"Pin-marker-filled","svgPath":"assets/icons/rebrand/Pin-marker-filled.svg"},{"name":"Pin-marker-in-line","svgPath":"assets/icons/rebrand/Pin-marker-in-line.svg"},{"name":"Plan-billing","svgPath":"assets/icons/Plan-billing.svg"},{"name":"Plan-billing-filled","svgPath":"assets/icons/rebrand/Plan-billing-filled.svg"},{"name":"Plan-billing-in-line","svgPath":"assets/icons/rebrand/Plan-billing-in-line.svg"},{"name":"Play-filled","svgPath":"assets/icons/rebrand/Play-filled.svg"},{"name":"Play-in-line","svgPath":"assets/icons/rebrand/Play-in-line.svg"},{"name":"Plus","svgPath":"assets/icons/Plus.svg"},{"name":"Plus-filled","svgPath":"assets/icons/rebrand/Plus-filled.svg"},{"name":"Plus-in-line","svgPath":"assets/icons/rebrand/Plus-in-line.svg"},{"name":"Portfolio-filled","svgPath":"assets/icons/rebrand/Portfolio-filled.svg"},{"name":"Portfolio-in-line","svgPath":"assets/icons/rebrand/Portfolio-in-line.svg"},{"name":"Premium","svgPath":"assets/icons/Premium.svg"},{"name":"Premium-circle-in-line","svgPath":"assets/icons/rebrand/Premium-circle-in-line.svg"},{"name":"Premium-filled","svgPath":"assets/icons/rebrand/Premium-filled.svg"},{"name":"Premium-in-line","svgPath":"assets/icons/rebrand/Premium-in-line.svg"},{"name":"Promotion","svgPath":"assets/icons/Promotion.svg"},{"name":"Question-count","svgPath":"assets/icons/Question-count.svg"},{"name":"Question-count-filled","svgPath":"assets/icons/rebrand/Question-count-filled.svg"},{"name":"Question-count-in-line","svgPath":"assets/icons/rebrand/Question-count-in-line.svg"},{"name":"Question-filled","svgPath":"assets/icons/rebrand/Question-filled.svg"},{"name":"Question-in-line","svgPath":"assets/icons/rebrand/Question-in-line.svg"},{"name":"Record-filled","svgPath":"assets/icons/rebrand/Record-filled.svg"},{"name":"Record-in-line","svgPath":"assets/icons/rebrand/Record-in-line.svg"},{"name":"Redeem-filled","svgPath":"assets/icons/rebrand/Redeem-filled.svg"},{"name":"Redeem-in-line","svgPath":"assets/icons/rebrand/Redeem-in-line.svg"},{"name":"Refer","svgPath":"assets/icons/Refer.svg"},{"name":"Refer-filled","svgPath":"assets/icons/rebrand/Refer-filled.svg"},{"name":"Refer-in-line","svgPath":"assets/icons/rebrand/Refer-in-line.svg"},{"name":"Reject-filled","svgPath":"assets/icons/rebrand/Reject-filled.svg"},{"name":"Reject-in-line","svgPath":"assets/icons/rebrand/Reject-in-line.svg"},{"name":"Reset","svgPath":"assets/icons/Reset.svg"},{"name":"Reset-filled","svgPath":"assets/icons/rebrand/Reset-filled.svg"},{"name":"Reset-in-line","svgPath":"assets/icons/rebrand/Reset-in-line.svg"},{"name":"Review","svgPath":"assets/icons/Review.svg"},{"name":"Review-emoji-1-filled","svgPath":"assets/icons/rebrand/Review-emoji-1-filled.svg"},{"name":"Review-emoji-1-in-line","svgPath":"assets/icons/rebrand/Review-emoji-1-in-line.svg"},{"name":"Review-emoji-2-filled","svgPath":"assets/icons/rebrand/Review-emoji-2-filled.svg"},{"name":"Review-emoji-2-in-line","svgPath":"assets/icons/rebrand/Review-emoji-2-in-line.svg"},{"name":"Review-emoji-3-filled","svgPath":"assets/icons/rebrand/Review-emoji-3-filled.svg"},{"name":"Review-emoji-3-in-line","svgPath":"assets/icons/rebrand/Review-emoji-3-in-line.svg"},{"name":"Review-emoji-4-filled","svgPath":"assets/icons/rebrand/Review-emoji-4-filled.svg"},{"name":"Review-emoji-4-in-line","svgPath":"assets/icons/rebrand/Review-emoji-4-in-line.svg"},{"name":"Review-emoji-5-filled","svgPath":"assets/icons/rebrand/Review-emoji-5-filled.svg"},{"name":"Review-emoji-5-in-line","svgPath":"assets/icons/rebrand/Review-emoji-5-in-line.svg"},{"name":"Review-filled","svgPath":"assets/icons/rebrand/Review-filled.svg"},{"name":"Review-half-star-filled","svgPath":"assets/icons/rebrand/Review-half-star-filled.svg"},{"name":"Review-half-star-in-line","svgPath":"assets/icons/rebrand/Review-half-star-in-line.svg"},{"name":"Review-in-line","svgPath":"assets/icons/rebrand/Review-in-line.svg"},{"name":"Review-star-filled","svgPath":"assets/icons/rebrand/Review-star-filled.svg"},{"name":"Review-star-filled-disabled","svgPath":"assets/icons/rebrand/Review-star-filled-disabled.svg"},{"name":"Review-star-in-line","svgPath":"assets/icons/rebrand/Review-star-in-line.svg"},{"name":"Round-check","svgPath":"assets/icons/Round-check.svg"},{"name":"Round-check-filled","svgPath":"assets/icons/Round-check-filled.svg"},{"name":"Round-check-in-line","svgPath":"assets/icons/rebrand/Round-check-in-line.svg"},{"name":"Rule-filled","svgPath":"assets/icons/rebrand/Rule-filled.svg"},{"name":"Rule-in-line","svgPath":"assets/icons/rebrand/Rule-in-line.svg"},{"name":"Search","svgPath":"assets/icons/Search.svg"},{"name":"Search-filled","svgPath":"assets/icons/rebrand/Search-filled.svg"},{"name":"Search-filled-AI","svgPath":"assets/icons/rebrand/Search-filled-AI.svg"},{"name":"Search-in-line","svgPath":"assets/icons/rebrand/Search-in-line.svg"},{"name":"Search-in-line-AI","svgPath":"assets/icons/rebrand/Search-in-line-AI.svg"},{"name":"Secure-checkout","svgPath":"assets/icons/Secure-checkout.svg"},{"name":"Secure-filled","svgPath":"assets/icons/rebrand/Secure-filled.svg"},{"name":"Secure-in-line","svgPath":"assets/icons/rebrand/Secure-in-line.svg"},{"name":"Send","svgPath":"assets/icons/Send.svg"},{"name":"Send-filled","svgPath":"assets/icons/rebrand/Send-filled.svg"},{"name":"Send-in-line","svgPath":"assets/icons/rebrand/Send-in-line.svg"},{"name":"Settings","svgPath":"assets/icons/Settings.svg"},{"name":"Settings-filled","svgPath":"assets/icons/rebrand/Settings-filled.svg"},{"name":"Settings-in-line","svgPath":"assets/icons/rebrand/Settings-in-line.svg"},{"name":"Share","svgPath":"assets/icons/Share.svg"},{"name":"Share-filled","svgPath":"assets/icons/rebrand/Share-filled.svg"},{"name":"Share-in-line","svgPath":"assets/icons/rebrand/Share-in-line.svg"},{"name":"Sidebar-close-filled","svgPath":"assets/icons/rebrand/Sidebar-close-filled.svg"},{"name":"Sidebar-close-in-line","svgPath":"assets/icons/rebrand/Sidebar-close-in-line.svg"},{"name":"Sidebar-open-filled","svgPath":"assets/icons/rebrand/Sidebar-open-filled.svg"},{"name":"Sidebar-open-in-line","svgPath":"assets/icons/rebrand/Sidebar-open-in-line.svg"},{"name":"Skip-next","svgPath":"assets/icons/Skip-next.svg"},{"name":"Skip-next-filled","svgPath":"assets/icons/rebrand/Skip-next-filled.svg"},{"name":"Skip-next-in-line","svgPath":"assets/icons/rebrand/Skip-next-in-line.svg"},{"name":"Skip-previous","svgPath":"assets/icons/Skip-previous.svg"},{"name":"Skip-previous-filled","svgPath":"assets/icons/rebrand/Skip-previous-filled.svg"},{"name":"Skip-previous-in-line","svgPath":"assets/icons/rebrand/Skip-previous-in-line.svg"},{"name":"Smart-criteria-filled","svgPath":"assets/icons/rebrand/Smart-criteria-filled.svg"},{"name":"Smart-criteria-in-line","svgPath":"assets/icons/rebrand/Smart-criteria-in-line.svg"},{"name":"Social-facebook","svgPath":"assets/icons/Social-facebook.svg"},{"name":"Social-instagram","svgPath":"assets/icons/Social-instagram.svg"},{"name":"Social-linkedin","svgPath":"assets/icons/Social-linkedin.svg"},{"name":"Sorting-down","svgPath":"assets/icons/Sorting-down.svg"},{"name":"Sorting-down-1","svgPath":"assets/icons/Sorting-down-1.svg"},{"name":"Sourcing-filled","svgPath":"assets/icons/rebrand/Sourcing-filled.svg"},{"name":"Sourcing-in-line","svgPath":"assets/icons/rebrand/Sourcing-in-line.svg"},{"name":"Sparkle-filled","svgPath":"assets/icons/rebrand/Sparkle-filled.svg"},{"name":"Sparkle-in-line","svgPath":"assets/icons/rebrand/Sparkle-in-line.svg"},{"name":"Sparkles","svgPath":"assets/icons/Sparkles.svg"},{"name":"Speedometer","svgPath":"assets/icons/Speedometer.svg"},{"name":"Speedometer-filled","svgPath":"assets/icons/rebrand/Speedometer-filled.svg"},{"name":"Speedometer-in-line","svgPath":"assets/icons/rebrand/Speedometer-in-line.svg"},{"name":"Star-filled","svgPath":"assets/icons/Star-filled.svg"},{"name":"Star-half","svgPath":"assets/icons/Star-half.svg"},{"name":"Star-in-line","svgPath":"assets/icons/rebrand/Star-in-line.svg"},{"name":"Star-outline","svgPath":"assets/icons/Star-outline.svg"},{"name":"Stop-filled","svgPath":"assets/icons/rebrand/Stop-filled.svg"},{"name":"Stop-in-line","svgPath":"assets/icons/rebrand/Stop-in-line.svg"},{"name":"Suitcase","svgPath":"assets/icons/Suitcase.svg"},{"name":"Summary-filled","svgPath":"assets/icons/rebrand/Summary-filled.svg"},{"name":"Summary-filled-AI","svgPath":"assets/icons/rebrand/Summary-filled-AI.svg"},{"name":"Summary-in-line","svgPath":"assets/icons/rebrand/Summary-in-line.svg"},{"name":"Summary-in-line-AI","svgPath":"assets/icons/rebrand/Summary-in-line-AI.svg"},{"name":"Support","svgPath":"assets/icons/Support.svg"},{"name":"Switch-filled","svgPath":"assets/icons/rebrand/Switch-filled.svg"},{"name":"Switch-in-line","svgPath":"assets/icons/rebrand/Switch-in-line.svg"},{"name":"Switch-tab","svgPath":"assets/icons/Switch-tab.svg"},{"name":"Switch-tab-filled","svgPath":"assets/icons/rebrand/Switch-tab-filled.svg"},{"name":"Switch-tab-in-line","svgPath":"assets/icons/rebrand/Switch-tab-in-line.svg"},{"name":"Sync","svgPath":"assets/icons/Sync.svg"},{"name":"Sync-filled","svgPath":"assets/icons/rebrand/Sync-filled.svg"},{"name":"Sync-in-line","svgPath":"assets/icons/rebrand/Sync-in-line.svg"},{"name":"Table-view-filled","svgPath":"assets/icons/rebrand/Table-view-filled.svg"},{"name":"Table-view-in-line","svgPath":"assets/icons/rebrand/Table-view-in-line.svg"},{"name":"Tag-filled","svgPath":"assets/icons/rebrand/Tag-filled.svg"},{"name":"Tag-filled-AI","svgPath":"assets/icons/rebrand/Tag-filled-AI.svg"},{"name":"Tag-in-line","svgPath":"assets/icons/rebrand/Tag-in-line.svg"},{"name":"Tag-in-line-AI","svgPath":"assets/icons/rebrand/Tag-in-line-AI.svg"},{"name":"Team","svgPath":"assets/icons/Team.svg"},{"name":"Team-filled","svgPath":"assets/icons/rebrand/Team-filled.svg"},{"name":"Team-in-line","svgPath":"assets/icons/rebrand/Team-in-line.svg"},{"name":"Test","svgPath":"assets/icons/Test.svg"},{"name":"Test-filled","svgPath":"assets/icons/rebrand/Test-filled.svg"},{"name":"Test-in-line","svgPath":"assets/icons/rebrand/Test-in-line.svg"},{"name":"TestGorilla","svgPath":"assets/icons/TestGorilla.svg"},{"name":"TestGorilla-filled","svgPath":"assets/icons/rebrand/TestGorilla-filled.svg"},{"name":"TestGorilla-in-line","svgPath":"assets/icons/rebrand/TestGorilla-in-line.svg"},{"name":"TestGorilla-Pink-filled","svgPath":"assets/icons/rebrand/TestGorilla-Pink-filled.svg"},{"name":"TestGorilla-Pink-in-line","svgPath":"assets/icons/rebrand/TestGorilla-Pink-in-line.svg"},{"name":"Thumb-average-filled","svgPath":"assets/icons/rebrand/Thumb-average-filled.svg"},{"name":"Thumb-average-in-line","svgPath":"assets/icons/rebrand/Thumb-average-in-line.svg"},{"name":"Thumb-down-filled","svgPath":"assets/icons/rebrand/Thumb-down-filled.svg"},{"name":"Thumb-down-in-line","svgPath":"assets/icons/rebrand/Thumb-down-in-line.svg"},{"name":"Thumb-up-filled","svgPath":"assets/icons/rebrand/Thumb-up-filled.svg"},{"name":"Thumb-up-in-line","svgPath":"assets/icons/rebrand/Thumb-up-in-line.svg"},{"name":"Thunder","svgPath":"assets/icons/Thunder.svg"},{"name":"Timer","svgPath":"assets/icons/Timer.svg"},{"name":"Timer-filled","svgPath":"assets/icons/rebrand/Timer-filled.svg"},{"name":"Timer-in-line","svgPath":"assets/icons/rebrand/Timer-in-line.svg"},{"name":"Trending-up-filled","svgPath":"assets/icons/rebrand/Trending-up-filled.svg"},{"name":"Trending-up-in-line","svgPath":"assets/icons/rebrand/Trending-up-in-line.svg"},{"name":"Trophy","svgPath":"assets/icons/Trophy.svg"},{"name":"Trophy-filled","svgPath":"assets/icons/rebrand/Trophy-filled.svg"},{"name":"Trophy-in-line","svgPath":"assets/icons/rebrand/Trophy-in-line.svg"},{"name":"Type","svgPath":"assets/icons/Type.svg"},{"name":"Type-filled","svgPath":"assets/icons/rebrand/Type-filled.svg"},{"name":"Type-in-line","svgPath":"assets/icons/rebrand/Type-in-line.svg"},{"name":"Unarchive","svgPath":"assets/icons/Unarchive.svg"},{"name":"Unarchive-filled","svgPath":"assets/icons/rebrand/Unarchive-filled.svg"},{"name":"Unarchive-in-line","svgPath":"assets/icons/rebrand/Unarchive-in-line.svg"},{"name":"Unlock","svgPath":"assets/icons/Unlock.svg"},{"name":"Unlock-filled","svgPath":"assets/icons/rebrand/Unlock-filled.svg"},{"name":"Unlock-in-line","svgPath":"assets/icons/rebrand/Unlock-in-line.svg"},{"name":"Unread-notification-filled","svgPath":"assets/icons/rebrand/Unread-notification-filled.svg"},{"name":"Unread-notification-in-line","svgPath":"assets/icons/rebrand/Unread-notification-in-line.svg"},{"name":"Upgrade","svgPath":"assets/icons/Upgrade.svg"},{"name":"Upgrade-filled","svgPath":"assets/icons/rebrand/Upgrade-filled.svg"},{"name":"Upgrade-in-line","svgPath":"assets/icons/rebrand/Upgrade-in-line.svg"},{"name":"Upgrade-paint-filled","svgPath":"assets/icons/rebrand/Upgrade-paint-filled.svg"},{"name":"Upgrade-paint-in-line","svgPath":"assets/icons/rebrand/Upgrade-paint-in-line.svg"},{"name":"Upload","svgPath":"assets/icons/Upload.svg"},{"name":"Upload-filled","svgPath":"assets/icons/rebrand/Upload-filled.svg"},{"name":"Upload-in-line","svgPath":"assets/icons/rebrand/Upload-in-line.svg"},{"name":"User-access","svgPath":"assets/icons/User-access.svg"},{"name":"User-access-filled","svgPath":"assets/icons/rebrand/User-access-filled.svg"},{"name":"User-access-in-line","svgPath":"assets/icons/rebrand/User-access-in-line.svg"},{"name":"User-add","svgPath":"assets/icons/User-add.svg"},{"name":"User-add-filled","svgPath":"assets/icons/rebrand/User-add-filled.svg"},{"name":"User-add-in-line","svgPath":"assets/icons/rebrand/User-add-in-line.svg"},{"name":"User-invite","svgPath":"assets/icons/User-invite.svg"},{"name":"User-profile","svgPath":"assets/icons/User-profile.svg"},{"name":"User-profile-filled","svgPath":"assets/icons/rebrand/User-profile-filled.svg"},{"name":"User-profile-in-line","svgPath":"assets/icons/rebrand/User-profile-in-line.svg"},{"name":"User-reject","svgPath":"assets/icons/User-reject.svg"},{"name":"User-switch","svgPath":"assets/icons/User-switch.svg"},{"name":"Validated-filled","svgPath":"assets/icons/rebrand/Validated-filled.svg"},{"name":"Validated-in-line","svgPath":"assets/icons/rebrand/Validated-in-line.svg"},{"name":"Verified-badge-filled","svgPath":"assets/icons/rebrand/Verified-badge-filled.svg"},{"name":"Verified-badge-in-line","svgPath":"assets/icons/rebrand/Verified-badge-in-line.svg"},{"name":"Video","svgPath":"assets/icons/Video.svg"},{"name":"Video-filled","svgPath":"assets/icons/rebrand/Video-filled.svg"},{"name":"Video-filled-AI","svgPath":"assets/icons/rebrand/Video-filled-AI.svg"},{"name":"Video-in-line","svgPath":"assets/icons/rebrand/Video-in-line.svg"},{"name":"Video-in-line-AI","svgPath":"assets/icons/rebrand/Video-in-line-AI.svg"},{"name":"Video-pause","svgPath":"assets/icons/Video-pause.svg"},{"name":"Video-play","svgPath":"assets/icons/Video-play.svg"},{"name":"Video-record","svgPath":"assets/icons/Video-record.svg"},{"name":"Video-stop","svgPath":"assets/icons/Video-stop.svg"},{"name":"View-filled","svgPath":"assets/icons/rebrand/View-filled.svg"},{"name":"View-in-line","svgPath":"assets/icons/rebrand/View-in-line.svg"},{"name":"Volume","svgPath":"assets/icons/Volume.svg"},{"name":"Volume-filled","svgPath":"assets/icons/rebrand/Volume-filled.svg"},{"name":"Volume-in-line","svgPath":"assets/icons/rebrand/Volume-in-line.svg"},{"name":"Warning","svgPath":"assets/icons/Warning.svg"},{"name":"Warning-filled","svgPath":"assets/icons/rebrand/Warning-filled.svg"},{"name":"Warning-in-line","svgPath":"assets/icons/rebrand/Warning-in-line.svg"},{"name":"Work-history-filled","svgPath":"assets/icons/rebrand/Work-history-filled.svg"},{"name":"Work-history-in-line","svgPath":"assets/icons/rebrand/Work-history-in-line.svg"},{"name":"Workspaces-filled","svgPath":"assets/icons/rebrand/Workspaces-filled.svg"},{"name":"Workspaces-in-line","svgPath":"assets/icons/rebrand/Workspaces-in-line.svg"},{"name":"Zoom-in","svgPath":"assets/icons/Zoom-in.svg"},{"name":"Zoom-in-filled","svgPath":"assets/icons/rebrand/Zoom-in-filled.svg"},{"name":"Zoom-in-in-line","svgPath":"assets/icons/rebrand/Zoom-in-in-line.svg"},{"name":"Zoom-out","svgPath":"assets/icons/Zoom-out.svg"},{"name":"Zoom-out-filled","svgPath":"assets/icons/rebrand/Zoom-out-filled.svg"},{"name":"Zoom-out-in-line","svgPath":"assets/icons/rebrand/Zoom-out-in-line.svg"}],"iconAliases":{"checkmark":"Check","tick":"Check","cross":"Close","x":"Close","dismiss":"Close","trash":"Delete","bin":"Delete","remove":"Delete","pencil":"Edit","warning":"Warning","alert":"Warning","info":"Info","informationcircle":"Info","chevrondown":"Arrow_down","chevronup":"Arrow_up","chevronleft":"Arrow_left","chevronright":"Arrow_right","caretdown":"Arrow_down","caretup":"Arrow_up","caretleft":"Arrow_left","caretright":"Arrow_right","arrowdown":"Arrow_down","arrowup":"Arrow_up","arrowleft":"Arrow_left","arrowright":"Arrow_right","plus":"Add","minus":"Minus","search":"Search","magnifier":"Search","eye":"Eye-view","eyeopen":"Eye-view","eyeclosed":"Eye-hide","eyeclose":"Eye-hide","hidden":"Eye-hide","visible":"Eye-view","user":"User-profile","person":"User-profile","people":"Team","group":"Team","calendar":"Calendar","date":"Calendar","settings":"Settings","gear":"Settings","cog":"Settings","filter":"Filter","download":"Download","upload":"Upload","fileupload":"File-upload","attachment":"File-attach","fileattach":"File-attach","documents":"Document","file":"Document","envelope":"Email-message","mail":"Email-message","email":"Email-message","message":"Chat","chat":"Chat","speech":"Chat","menu":"Menu-burger","hamburger":"Menu-burger","more":"Menu-ellipsis","options":"Menu-ellipsis","kebab":"Menu-ellipsis","morevertical":"Menu-ellipsis","morehorizontal":"Menu-ellipsis","dotsvertical":"Menu-ellipsis","dotshorizontal":"Menu-ellipsis","share":"Share","link":"Link","copy":"Copy","duplicate":"Clone","star":"Star-filled","favorite":"Star-filled","lock":"Lock","locked":"Lock","unlock":"Unlock","unlocked":"Unlock","trophy":"Trophy","language":"Language","play":"Video-play","pause":"Video-pause","stop":"Video-stop","refresh":"Sync","reload":"Sync","sync":"Sync"},"iconUnmapped":["heart","globe","world"],"iconFigmaMap":{"Actions/Menu-ellipses":{"canopy":"Menu-ellipsis","node":"313:7835","status":"mapped","note":"Figma spelling 'ellipses' vs code 'ellipsis'."},"Communication/Device":{"canopy":"Devices","node":"330:9984","status":"mapped","note":"Figma singular 'Device' vs code plural 'Devices'; without this it fuzzy-matched Device-rotate-in-line."},"Actions/ATS Pull":{"canopy":null,"node":"23036:428575","status":"figma-only"},"Actions/ATS Push":{"canopy":null,"node":"23080:428674","status":"figma-only"},"Actions/Publish":{"canopy":null,"node":"22790:50247","status":"figma-only"},"Actions/Screen-rotation":{"canopy":null,"node":"18950:45388","status":"figma-only"},"Actions/Replace":{"canopy":null,"node":"333:10325","status":"add-to-canopy"},"Communication/Home-dashboard":{"canopy":null,"node":"333:10359","status":"add-to-canopy"},"Communication/Talent":{"canopy":null,"node":"17490:42","status":"add-to-canopy"},"Communication/Tab-switch":{"canopy":"Switch-tab","node":"18448:44770","status":"mapped","note":"Figma 'Tab-switch' vs code 'Switch-tab' (word order flipped), so name normalization misses it."},"Status/Verified":{"canopy":null,"node":"12499:18331","status":"add-to-canopy","note":"Without this it fuzzy-matched ID-not-verified-filled."}},"tokens":[{"name":"background-color","value":"#f6f6f6","category":"color"},{"name":"font-family","value":"'Open Sans', sans-serif","category":"font"},{"name":"font-size","value":"14px","category":"font-size"},{"name":"tgo-teal-60b","value":"#1c443c","category":"color"},{"name":"tgo-teal-30b","value":"#31766a","category":"color"},{"name":"tgo-teal-default","value":"#46a997","category":"color"},{"name":"tgo-teal-30w","value":"#7ec3b6","category":"color"},{"name":"tgo-teal-60w","value":"#b5ddd5","category":"color"},{"name":"tgo-teal-secondary","value":"#cbd6cb","category":"color"},{"name":"tgo-teal-90w","value":"#ecf6f5","category":"color"},{"name":"tgo-petrol-60b","value":"#102930","category":"color"},{"name":"tgo-petrol-30b","value":"#1b4754","category":"color"},{"name":"tgo-petrol-default","value":"#276678","category":"color"},{"name":"tgo-petrol-30w","value":"#6894a0","category":"color"},{"name":"tgo-petrol-60w","value":"#a9c2c9","category":"color"},{"name":"tgo-petrol-secondary","value":"#c8d7de","category":"color"},{"name":"tgo-petrol-90w","value":"#e9f0f1","category":"color"},{"name":"tgo-error-60b","value":"#513131","category":"color"},{"name":"tgo-error-30b","value":"#8e5655","category":"color"},{"name":"tgo-error-60w","value":"#e3c3c6","category":"color"},{"name":"tgo-error-secondary","value":"#f0dad9","category":"color"},{"name":"tgo-error-default","value":"#cb7b7a","category":"color"},{"name":"tgo-warning-secondary","value":"#f0d6bb","category":"color"},{"name":"tgo-warning-default","value":"#cca45f","category":"color"},{"name":"tgo-black","value":"#000000","category":"color"},{"name":"tgo-dark","value":"#888888","category":"color"},{"name":"tgo-medium","value":"#e0e0e0","category":"color"},{"name":"tgo-grey","value":"#ededed","category":"color"},{"name":"tgo-light","value":"#f6f6f6","category":"color"},{"name":"tgo-white","value":"#ffffff","category":"color"},{"name":"box-shadow","value":"#00000014","category":"shadow"},{"name":"box-shadow-rebrand","value":"#24242414","category":"shadow"},{"name":"navigation-subtitle","value":"#528593","category":"color"},{"name":"brand-60","value":"#a90686","category":"color"},{"name":"brand-50","value":"#d410aa","category":"color"},{"name":"brand-40","value":"#ff54da","category":"color"},{"name":"brand-30","value":"#ffb3ee","category":"color"},{"name":"brand-20","value":"#ffe6fa","category":"color"},{"name":"brand-10","value":"#fff2fc","category":"color"},{"name":"grayscale-50","value":"#666666","category":"color"},{"name":"grayscale-40","value":"#919191","category":"color"},{"name":"grayscale-30","value":"#d3d3d3","category":"color"},{"name":"grayscale-20","value":"#e9e9e9","category":"color"},{"name":"grayscale-10","value":"#f4f4f4","category":"color"},{"name":"black","value":"#242424","category":"color"},{"name":"accent-20","value":"#fff5ed","category":"color"},{"name":"accent-10","value":"#fffaf6","category":"color"},{"name":"informative-50","value":"#1a47aa","category":"color"},{"name":"informative-40","value":"#0165fc","category":"color"},{"name":"informative-30","value":"#277cfd","category":"color"},{"name":"informative-20","value":"#99c1fe","category":"color"},{"name":"informative-10","value":"#d9e8ff","category":"color"},{"name":"success-50","value":"#3c9a13","category":"color"},{"name":"success-40","value":"#56c71a","category":"color"},{"name":"success-30","value":"#80d63e","category":"color"},{"name":"success-20","value":"#aae47a","category":"color"},{"name":"success-10","value":"#d4f2c3","category":"color"},{"name":"error-50","value":"#e02800","category":"color"},{"name":"error-40","value":"#ff3003","category":"color"},{"name":"error-30","value":"#ff6e4f","category":"color"},{"name":"error-20","value":"#ffac9a","category":"color"},{"name":"error-10","value":"#ffeae6","category":"color"},{"name":"warning-50","value":"#d07d00","category":"color"},{"name":"warning-40","value":"#ff9a03","category":"color"},{"name":"warning-30","value":"#ffc268","category":"color"},{"name":"warning-20","value":"#ffe1b3","category":"color"},{"name":"warning-10","value":"#fff5e6","category":"color"},{"name":"ai-gradient-blue","value":"#0165fc","category":"gradient"},{"name":"ai-gradient-pink","value":"#d410aa","category":"gradient"},{"name":"box-shadow-hover-state","value":"0px 4px 16px 0px #24242414","category":"shadow"},{"name":"box-shadow-notification","value":"0px 4px 16px 0px #2424240a","category":"shadow"},{"name":"box-shadow-page-card","value":"0px 4px 100px 0px #2424240a","category":"shadow"},{"name":"box-shadow-modal","value":"0px 4px 100px 0px #24242414","category":"shadow"}],"typography":[{"name":"body","selector":"body","source":"utility","properties":{"font-size":"14px","line-height":"20px"}},{"name":"body-large","selector":".body-large","source":"mixin","properties":{"font-weight":"bold","font-size":"14px","line-height":"22px"}},{"name":"body-large-bold","selector":".body-large-bold","source":"mixin","properties":{"font-weight":"bold","font-size":"16px","line-height":"24px"}},{"name":"body-large-regular","selector":".body-large-regular","source":"mixin","properties":{"font-weight":"400","font-size":"16px","line-height":"24px"}},{"name":"body-small-bold","selector":".body-small-bold","source":"mixin","properties":{"font-weight":"bold","font-size":"12px","line-height":"16px"}},{"name":"body-small-regular","selector":".body-small-regular","source":"mixin","properties":{"font-weight":"400","font-size":"12px","line-height":"16px"}},{"name":"button-label","selector":".button-label","source":"mixin","properties":{"font-weight":"500","font-size":"14px","font-feature-settings":"'capb'"}},{"name":"caption","selector":"caption,\n.caption","source":"utility","properties":{"font-size":"12px","line-height":"16px","display":"inline-block"}},{"name":"featured","selector":".featured","source":"utility","properties":{"font-size":"16px","line-height":"24px"}},{"name":"h1","selector":"h1,\n.h1","source":"heading","properties":{"font-size":"32px","line-height":"48px","font-weight":"400","color":"$black"}},{"name":"h2","selector":"h2,\n.h2","source":"heading","properties":{"font-size":"24px","line-height":"30px","color":"$black","font-weight":"900","font-family":"'ModernGothic', sans-serif"}},{"name":"h3","selector":"h3,\n.h3","source":"heading","properties":{"font-size":"20px","line-height":"26px","font-weight":"400","color":"$black"}},{"name":"h4","selector":"h4,\n.h4","source":"heading","properties":{"font-size":"16px","line-height":"20px","font-weight":"700","color":"$black","font-family":"'ModernGothic', sans-serif"}},{"name":"h5","selector":"h5,\n.h5","source":"heading","properties":{"font-size":"14px","line-height":"18px","font-weight":"700","color":"$black","font-family":"'ModernGothic', sans-serif"}},{"name":"h6","selector":"h6,\n.h6","source":"heading","properties":{"font-size":"12px","line-height":"16px","text-transform":"uppercase"}},{"name":"hover-state","selector":".hover-state","source":"mixin","properties":{"font-weight":"bold","text-decoration":"underline","font-size":"14px","line-height":"22px"}},{"name":"hyperlink","selector":".hyperlink","source":"mixin","properties":{"font-weight":"500","font-size":"14px","line-height":"22px"}},{"name":"hyperlink-large","selector":".hyperlink-large","source":"mixin","properties":{"font-weight":"500","font-size":"16px","line-height":"24px"}},{"name":"hyperlink-small","selector":".hyperlink-small","source":"mixin","properties":{"font-weight":"500","font-size":"12px","line-height":"16px"}},{"name":"label","selector":".label","source":"mixin","properties":{"font-weight":"400","font-size":"14px","font-feature-settings":"'capb'"}},{"name":"large-hover-state","selector":".large-hover-state","source":"mixin","properties":{"font-weight":"bold","text-decoration":"underline","font-size":"16px","line-height":"24px"}},{"name":"small","selector":"small","source":"utility","properties":{"font-size":"8px","line-height":"12px"}}],"colorPalettes":[{"name":"BrandColors","displayName":"Brand colors","colors":[{"variable":"$brand-60","cssClass":"brand-60","hex":"#A90686"},{"variable":"$brand-50","cssClass":"brand-50","hex":"#D410AA"},{"variable":"$brand-40","cssClass":"brand-40","hex":"#FF54DA"},{"variable":"$brand-30","cssClass":"brand-30","hex":"#FFB3EE"},{"variable":"$brand-20","cssClass":"brand-20","hex":"#FFE6FA"},{"variable":"$brand-10","cssClass":"brand-10","hex":"#FFF2FC"}]},{"name":"RebrandGrayscaleColors","displayName":"Grayscale colors (rebrand)","colors":[{"variable":"$grayscale-50","cssClass":"grayscale-50","hex":"#666666"},{"variable":"$grayscale-40","cssClass":"grayscale-40","hex":"#919191"},{"variable":"$grayscale-30","cssClass":"grayscale-30","hex":"#D3D3D3"},{"variable":"$grayscale-20","cssClass":"grayscale-20","hex":"#E9E9E9"},{"variable":"$grayscale-10","cssClass":"grayscale-10","hex":"#F4F4F4"}]},{"name":"RebrandBlackColor","displayName":"Black color (rebrand)","colors":[{"variable":"$black","cssClass":"black","hex":"#242424"}]},{"name":"RebrandAccentColors","displayName":"Accent colors (rebrand)","colors":[{"variable":"$accent-20","cssClass":"accent-20","hex":"#FFF5ED"},{"variable":"$accent-10","cssClass":"accent-10","hex":"#FFFAF6"}]},{"name":"RebrandInformativeColors","displayName":"Informative colors (rebrand)","colors":[{"variable":"$informative-50","cssClass":"informative-50","hex":"#1A47AA"},{"variable":"$informative-40","cssClass":"informative-40","hex":"#0165FC"},{"variable":"$informative-30","cssClass":"informative-30","hex":"#277CFD"},{"variable":"$informative-20","cssClass":"informative-20","hex":"#99C1FE"},{"variable":"$informative-10","cssClass":"informative-10","hex":"#D9E8FF"}]},{"name":"RebrandSuccessColors","displayName":"Success colors (rebrand)","colors":[{"variable":"$success-50","cssClass":"success-50","hex":"#3C9A13"},{"variable":"$success-40","cssClass":"success-40","hex":"#56C71A"},{"variable":"$success-30","cssClass":"success-30","hex":"#80D63E"},{"variable":"$success-20","cssClass":"success-20","hex":"#AAE47A"},{"variable":"$success-10","cssClass":"success-10","hex":"#D4F2C3"}]},{"name":"RebrandErrorColors","displayName":"Error colors (rebrand)","colors":[{"variable":"$error-50","cssClass":"error-50","hex":"#E02800"},{"variable":"$error-40","cssClass":"error-40","hex":"#FF3003"},{"variable":"$error-30","cssClass":"error-30","hex":"#FF6E4F"},{"variable":"$error-20","cssClass":"error-20","hex":"#FFAC9A"},{"variable":"$error-10","cssClass":"error-10","hex":"#FFEAE6"}]},{"name":"RebrandWarningColors","displayName":"Warning colors (rebrand)","colors":[{"variable":"$warning-50","cssClass":"warning-50","hex":"#D07D00"},{"variable":"$warning-40","cssClass":"warning-40","hex":"#FF9A03"},{"variable":"$warning-30","cssClass":"warning-30","hex":"#FFC268"},{"variable":"$warning-20","cssClass":"warning-20","hex":"#FFE1B3"},{"variable":"$warning-10","cssClass":"warning-10","hex":"#FFF5E6"}]},{"name":"AiColors","displayName":"Ai colors","colors":[{"variable":"ai-gradient","cssClass":"ai-gradient","hex":""}]}]}