From 0a0d8a572cd2256f009b536e409dc3e1896acd36 Mon Sep 17 00:00:00 2001 From: Olivia Tournois Date: Tue, 9 Oct 2018 11:58:07 +0200 Subject: [PATCH] feat(stark-ui): implementation of minimap component ISSUES CLOSED: #758t pu --- .../assets/styles/_old-variables.scss | 3 + packages/stark-ui/src/modules.ts | 1 + .../components/_app-menu.component.scss | 2 +- packages/stark-ui/src/modules/minimap.ts | 2 + .../modules/minimap/assets/translations/en.ts | 10 + .../modules/minimap/assets/translations/fr.ts | 10 + .../modules/minimap/assets/translations/nl.ts | 10 + .../src/modules/minimap/components.ts | 3 + .../minimap/components/_minimap-theme.scss | 196 +++++++++ .../components/_minimap.component.scss | 373 ++++++++++++++++++ .../components/item-properties.intf.ts | 14 + .../components/item-visibility.intf.ts | 17 + .../minimap/components/minimap.component.html | 55 +++ .../components/minimap.component.spec.ts | 133 +++++++ .../minimap/components/minimap.component.ts | 163 ++++++++ .../src/modules/minimap/minimap.module.ts | 37 ++ showcase/src/app/app.component.ts | 7 + showcase/src/app/app.module.ts | 2 + showcase/src/app/app.routes.ts | 2 + showcase/src/app/demo/demo.module.ts | 5 + showcase/src/app/demo/index.ts | 1 + .../demo/minimap/demo-minimap.component.html | 18 + .../demo/minimap/demo-minimap.component.scss | 0 .../demo/minimap/demo-minimap.component.ts | 48 +++ showcase/src/app/demo/minimap/index.ts | 1 + .../src/assets/examples/minimap/compact.html | 2 + .../src/assets/examples/minimap/compact.ts | 44 +++ .../src/assets/examples/minimap/full.html | 2 + showcase/src/assets/examples/minimap/full.ts | 40 ++ showcase/src/assets/translations/en.json | 5 + showcase/src/assets/translations/fr.json | 5 + showcase/src/assets/translations/nl.json | 5 + showcase/src/styles/_stark-styles.scss | 2 + 33 files changed, 1217 insertions(+), 1 deletion(-) create mode 100644 packages/stark-ui/src/modules/minimap.ts create mode 100644 packages/stark-ui/src/modules/minimap/assets/translations/en.ts create mode 100644 packages/stark-ui/src/modules/minimap/assets/translations/fr.ts create mode 100644 packages/stark-ui/src/modules/minimap/assets/translations/nl.ts create mode 100644 packages/stark-ui/src/modules/minimap/components.ts create mode 100644 packages/stark-ui/src/modules/minimap/components/_minimap-theme.scss create mode 100644 packages/stark-ui/src/modules/minimap/components/_minimap.component.scss create mode 100644 packages/stark-ui/src/modules/minimap/components/item-properties.intf.ts create mode 100644 packages/stark-ui/src/modules/minimap/components/item-visibility.intf.ts create mode 100644 packages/stark-ui/src/modules/minimap/components/minimap.component.html create mode 100644 packages/stark-ui/src/modules/minimap/components/minimap.component.spec.ts create mode 100644 packages/stark-ui/src/modules/minimap/components/minimap.component.ts create mode 100644 packages/stark-ui/src/modules/minimap/minimap.module.ts create mode 100644 showcase/src/app/demo/minimap/demo-minimap.component.html create mode 100644 showcase/src/app/demo/minimap/demo-minimap.component.scss create mode 100644 showcase/src/app/demo/minimap/demo-minimap.component.ts create mode 100644 showcase/src/app/demo/minimap/index.ts create mode 100644 showcase/src/assets/examples/minimap/compact.html create mode 100644 showcase/src/assets/examples/minimap/compact.ts create mode 100644 showcase/src/assets/examples/minimap/full.html create mode 100644 showcase/src/assets/examples/minimap/full.ts diff --git a/packages/stark-ui/assets/styles/_old-variables.scss b/packages/stark-ui/assets/styles/_old-variables.scss index 103f6f31ef..39d40e42e5 100644 --- a/packages/stark-ui/assets/styles/_old-variables.scss +++ b/packages/stark-ui/assets/styles/_old-variables.scss @@ -49,6 +49,7 @@ $md-primary-100: #b2d5ee; /* rgb(178, 213, 238) */ $md-primary: mat-color(map-get($base-theme, primary-palette)); $md-primary-600: mat-color(map-get($base-theme, primary-palette), 600); /* rgb(0, 106, 180) */ $md-primary-700: mat-color(map-get($base-theme, primary-palette), 700); /* rgb(0, 94, 160) */ +$md-primary-alpha-05: rgba(0, 118, 200, 0.05); $md-primary-alpha-10: rgba(0, 118, 200, 0.1); $md-primary-alpha-26: rgba(0, 118, 200, 0.26); $md-primary-alpha-50: rgba(0, 118, 200, 0.5); @@ -77,3 +78,5 @@ $md-warn-alpha-87: rgba(255, 87, 34, 0.87); $md-warn-200-alpha-38: rgba(255, 171, 145, 0.38); $cash2-offwhite: #f6f9fa; + +$checkbox-size: 15px; diff --git a/packages/stark-ui/src/modules.ts b/packages/stark-ui/src/modules.ts index 70b7e276da..be4d911c5d 100644 --- a/packages/stark-ui/src/modules.ts +++ b/packages/stark-ui/src/modules.ts @@ -11,6 +11,7 @@ export * from "./modules/date-range-picker"; export * from "./modules/dropdown"; export * from "./modules/keyboard-directives"; export * from "./modules/language-selector"; +export * from "./modules/minimap"; export * from "./modules/pagination"; export * from "./modules/pretty-print"; export * from "./modules/session-ui"; diff --git a/packages/stark-ui/src/modules/app-menu/components/_app-menu.component.scss b/packages/stark-ui/src/modules/app-menu/components/_app-menu.component.scss index 35a13fa960..a3ebbd96ac 100644 --- a/packages/stark-ui/src/modules/app-menu/components/_app-menu.component.scss +++ b/packages/stark-ui/src/modules/app-menu/components/_app-menu.component.scss @@ -44,4 +44,4 @@ } } -/* END stark-ui: src/modules/dropdown/components/dropdown/_dropdown.component.scss */ +/* END stark-ui: src/modules/dropdown/components/app-menu/_app-menu.component.scss */ diff --git a/packages/stark-ui/src/modules/minimap.ts b/packages/stark-ui/src/modules/minimap.ts new file mode 100644 index 0000000000..762a023587 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap.ts @@ -0,0 +1,2 @@ +export * from "./minimap/minimap.module"; +export * from "./minimap/components"; diff --git a/packages/stark-ui/src/modules/minimap/assets/translations/en.ts b/packages/stark-ui/src/modules/minimap/assets/translations/en.ts new file mode 100644 index 0000000000..4332bd70d5 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/assets/translations/en.ts @@ -0,0 +1,10 @@ +/** + * @ignore + */ +export const translationsEn: object = { + STARK: { + TABLE: { + TOGGLE_COLUMNS: "Column filters" + } + } +}; diff --git a/packages/stark-ui/src/modules/minimap/assets/translations/fr.ts b/packages/stark-ui/src/modules/minimap/assets/translations/fr.ts new file mode 100644 index 0000000000..c7f569269e --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/assets/translations/fr.ts @@ -0,0 +1,10 @@ +/** + * @ignore + */ +export const translationsFr: object = { + STARK: { + TABLE: { + TOGGLE_COLUMNS: "Filtre colonnes" + } + } +}; diff --git a/packages/stark-ui/src/modules/minimap/assets/translations/nl.ts b/packages/stark-ui/src/modules/minimap/assets/translations/nl.ts new file mode 100644 index 0000000000..8267ee402b --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/assets/translations/nl.ts @@ -0,0 +1,10 @@ +/** + * @ignore + */ +export const translationsNl: object = { + STARK: { + TABLE: { + TOGGLE_COLUMNS: "Kolom filters" + } + } +}; diff --git a/packages/stark-ui/src/modules/minimap/components.ts b/packages/stark-ui/src/modules/minimap/components.ts new file mode 100644 index 0000000000..8fc0adbf67 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components.ts @@ -0,0 +1,3 @@ +export * from "./components/minimap.component"; +export * from "./components/item-properties.intf"; +export * from "./components/item-visibility.intf"; diff --git a/packages/stark-ui/src/modules/minimap/components/_minimap-theme.scss b/packages/stark-ui/src/modules/minimap/components/_minimap-theme.scss new file mode 100644 index 0000000000..565efe2b89 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/_minimap-theme.scss @@ -0,0 +1,196 @@ +/* ============================================================================== */ +/* S t a r k M i n i m a p */ +/* ============================================================================== */ +/* stark-ui: src/modules/minimap/components/minimap-theme.scss */ + +.compact-minimap { + & .stark-minimap-dots { + & i { + background-color: $secondary-dark-text-color; + opacity: 0.2; + } + & li.selected i { + opacity: 1; + } + } + & .stark-minimap-dropdown-toggle { + & .stark-minimap-dropdown-toggle-menu { + box-shadow: $elevation-4; + & mat-checkbox.mat-checked .mat-icon { + background-color: $md-accent; + } + } + & .mat-button.mat-icon-button { + & svg { + fill: #000; + } + border: 1px solid mat-color($grey-palette, 300); + background-color: $md-primary-alpha-05; + } + & mat-icon { + fill: $md-primary; + } + & .stark-minimap-dropdown-toggle-menu { + border-color: mat-color($grey-palette, 500); + box-shadow: 0 1px 2px mat-color($grey-palette, 300); + background-color: mat-color($grey-palette, 50); + & mat-checkbox { + &:hover { + background: $md-primary-alpha-05; + } + & ._mat-icon { + &::after { + border-color: mat-color($grey-palette, 50); + } + } + & .stark-minimap-dropdown { + & mat-checkbox.stark-minimap-column-checkbox { + &.mat-checked ._mat-icon { + background-color: $md-primary-200-alpha-38; + } + } + } + } + } + + @media #{$desktop-query} { + & .stark-minimap-dropdown-toggle-menu { + font-size: 16px; + & mat-checkbox { + padding: 3px 16px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 10px; + left: 16px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + } + & ._mat-label { + width: 100%; + padding: 0 0 0 26px; + font-size: 12px; + & span { + max-width: 400px; + } + } + &.mat-checked ._mat-icon::after { + width: 8px; + height: 10px; + top: 2px; + left: 4px; + } + &:first-child { + margin-top: 8px; + } + &:last-child { + margin-bottom: 8px; + } + } + } + } + } +} + +.full-minimap { + & .stark-minimap-dots { + & i { + background-color: $secondary-dark-text-color; + opacity: 0.2; + } + & li.selected i { + opacity: 1; + } + } + & .stark-minimap-dropdown-toggle { + & .stark-minimap-dropdown-toggle-menu { + box-shadow: $elevation-4; + & mat-checkbox.mat-checked .mat-icon { + background-color: $md-accent; + } + } + & .mat-button.mat-icon-button { + & svg { + fill: #000; + } + border: 1px solid mat-color($grey-palette, 300); + background-color: $md-primary-alpha-05; + } + & mat-icon { + fill: $md-primary; + } + & .stark-minimap-dropdown-toggle-menu { + border-color: mat-color($grey-palette, 500); + box-shadow: 0 1px 2px mat-color($grey-palette, 300); + background-color: mat-color($grey-palette, 50); + & mat-checkbox { + &:hover { + background: $md-primary-alpha-05; + } + & ._mat-icon { + &::after { + border-color: mat-color($grey-palette, 50); + } + } + & .stark-minimap-dropdown { + & mat-checkbox.stark-minimap-column-checkbox { + &.mat-checked ._mat-icon { + background-color: $md-primary-200-alpha-38; + } + } + } + } + } + + @media #{$desktop-query} { + & .stark-minimap-dropdown-toggle-menu { + font-size: 16px; + & mat-checkbox { + padding: 3px 16px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 10px; + left: 16px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + } + & ._mat-label { + width: 100%; + padding: 0 0 0 26px; + font-size: 12px; + & span { + max-width: 400px; + } + } + &.mat-checked ._mat-icon::after { + width: 8px; + height: 10px; + top: 2px; + left: 4px; + } + &:first-child { + margin-top: 8px; + } + &:last-child { + margin-bottom: 8px; + } + } + } + } + } +} + +.stark-table { + & .stark-minimap { + & .stark-minimap-dropdown-toggle .stark-minimap-dropdown-toggle-menu { + box-shadow: $elevation-2; + } + } +} + +/* End stark-ui: src/modules/minimap/components/minimap-theme.scss */ diff --git a/packages/stark-ui/src/modules/minimap/components/_minimap.component.scss b/packages/stark-ui/src/modules/minimap/components/_minimap.component.scss new file mode 100644 index 0000000000..d1a90d8e67 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/_minimap.component.scss @@ -0,0 +1,373 @@ +/* ============================================================================== */ +/* S t a r k M i n i m a p */ +/* ============================================================================== */ +/* stark-ui: src/modules/minimap/components/minimap.component.scss */ + +.compact-minimap { + display: flex; + flex-flow: row wrap; + flex-grow: 1; + align-items: flex-start; + justify-content: flex-start; + padding-top: 8px; + margin: 0 15px; + & .stark-minimap-dots { + & ul { + margin: 0 8px 0 0; + padding: 0; + } + & li { + display: inline-block; + margin: 0; + padding: 0 3px 0; + line-height: 18px; + vertical-align: top; + } + & i { + display: inline-block; + width: 4px; + height: 4px; + opacity: 0.2; + } + & li.selected i { + opacity: 1; + } + } + & .stark-minimap-dropdown-toggle { + position: relative; + margin: 0; + padding-top: 0; + & .stark-minimap-dropdown-toggle-menu { + font-size: 14px; + border: 0; + min-width: 180px; + & mat-checkbox .mat-label { + margin-left: 40px; + font-size: 12px; + } + } + & .mat-button.mat-icon-button { + height: auto; + margin: 0; + transform: translateY(-8px); + } + & .mat-icon-button .up { + display: none; + } + &.visible { + & .stark-minimap-dropdown-toggle-menu { + display: block; + } + & .down { + display: none; + } + & .up { + display: inline-block; + } + } + & .stark-minimap-dropdown-toggle-button { + min-height: 0; + margin: 0; + padding: 0 2px 0 0; + } + & .stark-minimap-dropdown-toggle-menu { + position: absolute; + z-index: 1; + top: 27px; + right: 0; + display: none; + padding: 0; + border-radius: 2px; + font-size: 16px; + & mat-checkbox { + display: block; + min-width: 0; + min-height: 0; + margin: 0; + padding: 4px 8px 5px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 13px; + left: 8px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + top: 5px; + } + & ._mat-label { + width: 100%; + margin: 0; + padding: 0 0 0 20px; + & span { + display: block; + max-width: 260px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + &.mat-checked ._mat-icon::after { + width: 6px; + height: 8px; + top: 3px; + left: 4px; + } + &:first-child { + margin-top: 4px; + } + &:last-child { + margin-bottom: 4px; + } + } + & .stark-minimap-dropdown { + & .mat-container { + left: 16px; + } + & mat-checkbox.stark-minimap-column-checkbox { + & ._mat-icon { + border-width: 1px; + } + } + } + } + } + + @media #{$desktop-query} { + & .stark-minimap-dropdown-toggle-menu { + font-size: 16px; + & mat-checkbox { + padding: 3px 16px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 10px; + left: 16px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + } + & ._mat-label { + width: 100%; + padding: 0 0 0 26px; + font-size: 12px; + & span { + max-width: 400px; + } + } + &.mat-checked ._mat-icon::after { + width: 8px; + height: 10px; + top: 2px; + left: 4px; + } + &:first-child { + margin-top: 8px; + } + &:last-child { + margin-bottom: 8px; + } + } + } + } +} + +.full-minimap { + display: flex; + flex-flow: row wrap; + flex-grow: 1; + align-items: flex-start; + justify-content: flex-start; + padding-top: 8px; + margin: 0 15px; + & .stark-minimap-dots { + & ul { + margin: 0 8px 0 0; + padding: 0; + } + & li { + display: inline-block; + margin: 0; + padding: 0 3px 0; + line-height: 18px; + vertical-align: top; + } + & i { + display: inline-block; + width: 4px; + height: 4px; + opacity: 0.2; + } + & li.selected i { + opacity: 1; + } + } + & .stark-minimap-dropdown-toggle { + position: relative; + margin: 0; + padding-top: 0; + & .stark-minimap-dropdown-toggle-menu { + font-size: 14px; + border: 0; + min-width: 180px; + & mat-checkbox .mat-label { + margin-left: 40px; + font-size: 12px; + } + } + & .mat-button.mat-icon-button { + height: auto; + margin: 0; + transform: translateY(-8px); + } + & .mat-icon-button .up { + display: none; + } + &.visible { + & .stark-minimap-dropdown-toggle-menu { + display: block; + } + & .down { + display: none; + } + & .up { + display: inline-block; + } + } + & .stark-minimap-dropdown-toggle-button { + min-height: 0; + margin: 0; + padding: 0 2px 0 0; + } + & .stark-minimap-dropdown-toggle-menu { + position: absolute; + z-index: 1; + top: 27px; + right: 0; + display: none; + padding: 0; + border-radius: 2px; + font-size: 16px; + & mat-checkbox { + display: block; + min-width: 0; + min-height: 0; + margin: 0; + padding: 4px 8px 5px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 13px; + left: 8px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + top: 5px; + } + & ._mat-label { + width: 100%; + margin: 0; + padding: 0 0 0 20px; + & span { + display: block; + max-width: 260px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + &.mat-checked ._mat-icon::after { + width: 6px; + height: 8px; + top: 3px; + left: 4px; + } + &:first-child { + margin-top: 4px; + } + &:last-child { + margin-bottom: 4px; + } + } + & .stark-minimap-dropdown { + & .mat-container { + left: 16px; + } + & mat-checkbox.stark-minimap-column-checkbox { + & ._mat-icon { + border-width: 1px; + } + } + } + } + } + + @media #{$desktop-query} { + & .stark-minimap-dropdown-toggle-menu { + font-size: 16px; + & mat-checkbox { + padding: 3px 16px; + & ._mat-container { + width: $checkbox-size; + height: $checkbox-size; + top: 10px; + left: 16px; + } + & ._mat-icon { + width: $checkbox-size; + height: $checkbox-size; + } + & ._mat-label { + width: 100%; + padding: 0 0 0 26px; + font-size: 12px; + & span { + max-width: 400px; + } + } + &.mat-checked ._mat-icon::after { + width: 8px; + height: 10px; + top: 2px; + left: 4px; + } + &:first-child { + margin-top: 8px; + } + &:last-child { + margin-bottom: 8px; + } + } + } + } +} + +.stark-table { + & .stark-minimap-dropdown-toggle-menu { + padding-right: 16px; + } + & .stark-table-action-bar { + & .stark-minimap-dropdown-toggle-button { + & mat-icon { + width: 20px; + height: 20px; + } + } + } + & .stark-minimap { + padding-top: 0; + & .stark-minimap-dropdown-toggle .mat-button.mat-icon-button { + transform: none; + } + & .stark-minimap-dropdown-toggle .stark-minimap-dropdown-toggle-menu { + font-size: 14px; + border: 0; + min-width: 180px; + } + } +} + +/* END stark-ui: src/modules/minimap/components/minimap.component.scss */ diff --git a/packages/stark-ui/src/modules/minimap/components/item-properties.intf.ts b/packages/stark-ui/src/modules/minimap/components/item-properties.intf.ts new file mode 100644 index 0000000000..5635504787 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/item-properties.intf.ts @@ -0,0 +1,14 @@ +/** + * Represents the properties of an item inside the Stark Minimap component. + */ +export interface StarkMinimapItemProperties { + /** + * The name of the item + */ + name: string; + + /** + * The label of the item (optional). If not provided, then the name of the item will be used. + */ + label?: string; +} diff --git a/packages/stark-ui/src/modules/minimap/components/item-visibility.intf.ts b/packages/stark-ui/src/modules/minimap/components/item-visibility.intf.ts new file mode 100644 index 0000000000..cc1ad68b4f --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/item-visibility.intf.ts @@ -0,0 +1,17 @@ +import { StarkMinimapItemProperties } from "./item-properties.intf"; + +/** + * Indicates if a item is visible or not + */ +export interface ItemVisibility { + /** + * is the item visible + */ + isVisible: boolean; + + /** + * the item to display/hide + */ + + item: StarkMinimapItemProperties; +} diff --git a/packages/stark-ui/src/modules/minimap/components/minimap.component.html b/packages/stark-ui/src/modules/minimap/components/minimap.component.html new file mode 100644 index 0000000000..a64f3a5f82 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/minimap.component.html @@ -0,0 +1,55 @@ +
+
+ +
+
+ + {{ getItemLabel(itemToHandle) }} + +
+
+
+
+ +
+
+
    +
  • + +
  • +
+
+
+ +
+
+ + {{ getItemLabel(itemToHandle) }} + +
+
+
+
+
+ diff --git a/packages/stark-ui/src/modules/minimap/components/minimap.component.spec.ts b/packages/stark-ui/src/modules/minimap/components/minimap.component.spec.ts new file mode 100644 index 0000000000..5318ac287c --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/minimap.component.spec.ts @@ -0,0 +1,133 @@ +//tslint:disable:no-commented-code completed-docs +import { StarkMinimapComponent } from "./minimap.component"; +import { StarkMinimapItemProperties } from "./item-properties.intf"; +import createSpyObj = jasmine.createSpyObj; +import SpyObj = jasmine.SpyObj; +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormsModule } from "@angular/forms"; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { TranslateModule, TranslateService } from "@ngx-translate/core"; +import { STARK_LOGGING_SERVICE } from "@nationalbankbelgium/stark-core"; +import { MockStarkLoggingService } from "@nationalbankbelgium/stark-core/testing"; +import { Component, EventEmitter, NO_ERRORS_SCHEMA, ViewChild } from "@angular/core"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { ItemVisibility } from "@nationalbankbelgium/stark-ui"; + +@Component({ + selector: `host-component`, + template: `` +}) +class TestHostComponent { + @ViewChild(StarkMinimapComponent) + public minimapComponent: StarkMinimapComponent; + public items: StarkMinimapItemProperties[]; + public visibleItems: string[]; +} + +/* tslint:disable:no-big-function */ +describe("MinimapComponent", () => { + let component: StarkMinimapComponent; + let hostComponent: TestHostComponent; + let hostFixture: ComponentFixture; + let defaultItem: StarkMinimapItemProperties; + let defaultItemVisibility: ItemVisibility; + + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [FormsModule, MatCheckboxModule, MatTooltipModule, NoopAnimationsModule, TranslateModule.forRoot()], + declarations: [StarkMinimapComponent, TestHostComponent], + providers: [{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() }, TranslateService], + schemas: [NO_ERRORS_SCHEMA] // to avoid errors due to "mat-icon" directive not known (which we don't want to add in these tests) + }).compileComponents(); + })); + + beforeEach(() => { + hostFixture = TestBed.createComponent(TestHostComponent); + hostComponent = hostFixture.componentInstance; + hostFixture.detectChanges(); + + component = hostComponent.minimapComponent; + }); + + describe("on initialization", () => { + it("should set internal component properties", () => { + expect(hostFixture).toBeDefined(); + expect(component).toBeDefined(); + + expect(component.logger).not.toBeNull(); + expect(component.logger).toBeDefined(); + expect(component.items).toBeUndefined(); + expect(component.visibleItems).toBeUndefined(); + }); + }); + + describe("toggleDropdownMenu", () => { + it("should set TRUE if customMenu is displayed", () => { + component.isDisplayedMenu = false; + component.toggleDropdownMenu(); + expect(component.isDisplayedMenu).toBe(true); + }); + + it("should set FALSE if customMenu is hidden", () => { + component.isDisplayedMenu = true; + component.toggleDropdownMenu(); + expect(component.isDisplayedMenu).toBe(false); + }); + }); + + describe("getItemLabel", () => { + it("should return the label of item when it is declared in minimapItemsProperties", () => { + const getItemLabel: string = component.getItemLabel({ name: "column1", label: "Test label" }); + expect(getItemLabel).toEqual("Test label"); + }); + + it("should return the item name of item when it is not declared in minimapItemsProperties", () => { + const getItemLabel: string = component.getItemLabel({ name: "column2" }); + expect(getItemLabel).toEqual("column2"); + }); + }); + + describe("isItemVisible", () => { + beforeEach(() => { + component.items = []; + component.visibleItems = []; + defaultItem = { + name: "item" + }; + }); + + it("should return FALSE if item is NOT present in visibleItems array", () => { + const isVisibleColumn: boolean = component.isItemVisible(defaultItem); + expect(isVisibleColumn).toBe(false); + }); + + it("should return TRUE if item is present in visibleItems array", () => { + component.visibleItems.push(defaultItem.name); + const isVisibleColumn: boolean = component.isItemVisible(defaultItem); + expect(isVisibleColumn).toBe(true); + }); + }); + + describe("triggerShowHideItem", () => { + beforeEach(() => { + defaultItemVisibility = { + isVisible: true, + item: { + name: "item" + } + }; + }); + + it("should trigger the callback showHideItem method with item as argument if callback is defined", () => { + const showHideItemSpy: SpyObj> = createSpyObj("showHideItem", ["emit"]); + component.items = []; + component.visibleItems = []; + component.showHideItem = showHideItemSpy; + component.triggerShowHideItem(defaultItem); + + expect(showHideItemSpy.emit).toHaveBeenCalledTimes(1); + expect(showHideItemSpy.emit).toHaveBeenCalledWith(defaultItemVisibility); + }); + }); +}); diff --git a/packages/stark-ui/src/modules/minimap/components/minimap.component.ts b/packages/stark-ui/src/modules/minimap/components/minimap.component.ts new file mode 100644 index 0000000000..fb4eb68c97 --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/components/minimap.component.ts @@ -0,0 +1,163 @@ +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { + ApplicationRef, + Component, + ElementRef, + EventEmitter, + Inject, + Input, + OnDestroy, + OnInit, + Output, + Renderer2, + ViewEncapsulation +} from "@angular/core"; +import { ItemVisibility, StarkDOMUtil, StarkMinimapItemProperties } from "@nationalbankbelgium/stark-ui"; +import { AbstractStarkUiComponent } from "../../../common/classes/abstract-component"; + +export type StarkMinimapComponentMode = "compact"; + +/** + * Name of the component + */ +const componentName: string = "stark-minimap"; + +/** + * Component to display a minimap which permits to toggle the visibility of passed elements. + * The minimap shows the label of the elements to display with a checkbox to enable/disable the visibility + */ +@Component({ + selector: "stark-minimap", + templateUrl: "./minimap.component.html", + encapsulation: ViewEncapsulation.None, + host: { + class: componentName + } +}) +export class StarkMinimapComponent extends AbstractStarkUiComponent implements OnInit, OnDestroy { + /** + * Array of StarkMinimapItemProperties objects which define the items to display in the minimap. + */ + @Input() + public items: StarkMinimapItemProperties[]; + + /** + * Array of names of the items that are visible. + */ + @Input() + public visibleItems: string[]; + + /** + * the minimap mode we want to display + */ + @Input() + public mode?: StarkMinimapComponentMode; + + /** + * Output that will emit the selected element to be shown/hidden + */ + @Output() + public showHideItem: EventEmitter = new EventEmitter(); + + public windowClickHandler: EventListener; + public isDisplayedMenu: boolean; + + public constructor( + @Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService, + protected renderer: Renderer2, + protected elementRef: ElementRef, + protected applicationRef: ApplicationRef + ) { + super(renderer, elementRef); + } + + public ngOnInit(): void { + super.ngOnInit(); + this.isDisplayedMenu = false; + this.attachWindowClickHandler(); + } + + /** + * Component lifecycle hook + */ + public ngOnDestroy(): void { + this.logger.debug(componentName + ": removing clickHandler"); + this.removeWindowClickHandler(); + } + + /** + * Retrieve the label of an item + * @param itemToHandle - the item whose label we want to retrieve + * @returns the label of the item + */ + public getItemLabel(itemToHandle: StarkMinimapItemProperties): string { + if (itemToHandle.label) { + return itemToHandle.label; + } + + return itemToHandle.name; + } + + /** + * Attach a handler when a click is performed on the window + */ + public attachWindowClickHandler(): void { + this.windowClickHandler = (event: Event) => { + if (this.isDisplayedMenu) { + const parentElement: Element | undefined = StarkDOMUtil.searchParentElementByClass( + event.target, + "stark-minimap-dropdown-toggle" + ); + + if (!parentElement) { + this.toggleDropdownMenu(); + this.applicationRef.tick(); + } + } + }; + + window.addEventListener("click", this.windowClickHandler); + } + + /** + * remove the handler attached to a window + */ + public removeWindowClickHandler(): void { + window.removeEventListener("click", this.windowClickHandler); + } + + /** + * Return true/false if the given item is already visible or if the priority (if specified) for such item is not "hidden" + * Otherwise, the item is considered to be hidden by default + */ + public isItemVisible(itemToHandle: StarkMinimapItemProperties): boolean { + return this.visibleItems instanceof Array ? this.visibleItems.indexOf(itemToHandle.name) > -1 : false; + } + + /** + * Display / Hide the dropdown menu + */ + public toggleDropdownMenu(): void { + this.isDisplayedMenu = !this.isDisplayedMenu; + } + + /** + * triggers the show/hide event on an item + * @param itemToHandle : item - the item to show/hide + */ + public triggerShowHideItem(itemToHandle: StarkMinimapItemProperties): void { + const visibleItem: ItemVisibility = { + isVisible: !this.isItemVisible(itemToHandle), + item: itemToHandle + }; + this.showHideItem.emit(visibleItem); + } + + /** + * @ignore + */ + public trackItemFn(_itemToHandle: any): string { + // FIXME: cannot call areSimpleTypes() from the component since this track function gets no context + return _itemToHandle; + } +} diff --git a/packages/stark-ui/src/modules/minimap/minimap.module.ts b/packages/stark-ui/src/modules/minimap/minimap.module.ts new file mode 100644 index 0000000000..c7fcdd5b4e --- /dev/null +++ b/packages/stark-ui/src/modules/minimap/minimap.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from "@angular/core"; +import { FormsModule } from "@angular/forms"; +import { StarkMinimapComponent } from "./components/minimap.component"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { translationsEn } from "./assets/translations/en"; +import { translationsFr } from "./assets/translations/fr"; +import { translationsNl } from "./assets/translations/nl"; +import { MatIconModule } from "@angular/material/icon"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { CommonModule } from "@angular/common"; +import { TranslateModule, TranslateService } from "@ngx-translate/core"; +import { mergeTranslations, StarkLocale } from "@nationalbankbelgium/stark-core"; + +@NgModule({ + declarations: [StarkMinimapComponent], + imports: [ + CommonModule, + BrowserAnimationsModule, + FormsModule, + MatButtonModule, + MatCheckboxModule, + MatIconModule, + MatTooltipModule, + TranslateModule + ], + exports: [StarkMinimapComponent] +}) +export class StarkMinimapModule { + public constructor(private translateService: TranslateService) { + const english: StarkLocale = { languageCode: "en", translations: translationsEn }; + const french: StarkLocale = { languageCode: "fr", translations: translationsFr }; + const dutch: StarkLocale = { languageCode: "nl", translations: translationsNl }; + mergeTranslations(this.translateService, english, french, dutch); + } +} diff --git a/showcase/src/app/app.component.ts b/showcase/src/app/app.component.ts index c4fbf87a05..6d83f2ba1a 100644 --- a/showcase/src/app/app.component.ts +++ b/showcase/src/app/app.component.ts @@ -140,6 +140,13 @@ export class AppComponent implements OnInit { isEnabled: true, targetState: "demo-menu" }, + { + id: "menu-stark-ui-components-minimap", + label: "Minimap", + isVisible: true, + isEnabled: true, + targetState: "demo-minimap" + }, { id: "menu-stark-ui-components-pagination", label: "Pagination", diff --git a/showcase/src/app/app.module.ts b/showcase/src/app/app.module.ts index 320369aa1d..79141b8d59 100644 --- a/showcase/src/app/app.module.ts +++ b/showcase/src/app/app.module.ts @@ -59,6 +59,7 @@ import { StarkAppSidebarModule, StarkBreadcrumbModule, StarkDatePickerModule, + StarkMinimapModule, StarkLanguageSelectorModule, StarkSessionUiModule, StarkSvgViewBoxModule, @@ -236,6 +237,7 @@ export const metaReducers: MetaReducer[] = ENV !== "production" ? [logger StarkSvgViewBoxModule, StarkDatePickerModule, StarkBreadcrumbModule, + StarkMinimapModule, StarkToastNotificationModule.forRoot({ delay: 5000, position: "top right", diff --git a/showcase/src/app/app.routes.ts b/showcase/src/app/app.routes.ts index 621464fa78..3ecfdfd036 100644 --- a/showcase/src/app/app.routes.ts +++ b/showcase/src/app/app.routes.ts @@ -14,6 +14,7 @@ import { DemoKeyboardDirectivesComponent, DemoLanguageSelectorComponent, DemoLogoutComponent, + DemoMinimapComponent, DemoMenuComponent, DemoPaginationComponent, DemoPrettyPrintComponent, @@ -46,6 +47,7 @@ export const APP_STATES: Ng2StateDeclaration[] = [ { name: "demo-keyboard-directives", url: "/demo/keyboard-directives", component: DemoKeyboardDirectivesComponent }, { name: "demo-language-selector", url: "/demo/language-selector", component: DemoLanguageSelectorComponent }, { name: "demo-logout", url: "/demo/logout", component: DemoLogoutComponent }, + { name: "demo-minimap", url: "/demo/minimap", component: DemoMinimapComponent }, { name: "demo-menu", url: "/demo/menu", component: DemoMenuComponent }, { name: "demo-pagination", url: "/demo/pagination", component: DemoPaginationComponent }, { name: "demo-pretty-print", url: "/demo/pretty-print", component: DemoPrettyPrintComponent }, diff --git a/showcase/src/app/demo/demo.module.ts b/showcase/src/app/demo/demo.module.ts index ee6f4825b0..14b2b347dc 100644 --- a/showcase/src/app/demo/demo.module.ts +++ b/showcase/src/app/demo/demo.module.ts @@ -31,6 +31,7 @@ import { DemoKeyboardDirectivesComponent } from "./keyboard-directives/demo-keyb import { DemoLanguageSelectorComponent } from "./language-selector/demo-language-selector.component"; import { DemoLogoutComponent } from "./logout/demo-logout.component"; import { DemoMenuComponent } from "./menu/demo-menu.component"; +import { DemoMinimapComponent } from "./minimap/demo-minimap.component"; import { DemoPaginationComponent } from "./pagination/demo-pagination.component"; import { DemoPrettyPrintComponent } from "./pretty-print/demo-pretty-print.component"; import { DemoSliderComponent } from "./slider/demo-slider.component"; @@ -46,6 +47,7 @@ import { StarkDatePickerModule, StarkDateRangePickerModule, StarkDropdownModule, + StarkMinimapModule, StarkKeyboardDirectivesModule, StarkLanguageSelectorModule, StarkAppMenuModule, @@ -83,6 +85,7 @@ import { StarkDateRangePickerModule, StarkDropdownModule, StarkKeyboardDirectivesModule, + StarkMinimapModule, StarkLanguageSelectorModule, StarkPaginationModule, StarkPrettyPrintModule, @@ -107,6 +110,7 @@ import { DemoLanguageSelectorComponent, DemoLogoutComponent, DemoMenuComponent, + DemoMinimapComponent, DemoPaginationComponent, DemoPrettyPrintComponent, DemoSidebarComponent, @@ -131,6 +135,7 @@ import { DemoKeyboardDirectivesComponent, DemoLanguageSelectorComponent, DemoLogoutComponent, + DemoMinimapComponent, DemoMenuComponent, DemoPaginationComponent, DemoPrettyPrintComponent, diff --git a/showcase/src/app/demo/index.ts b/showcase/src/app/demo/index.ts index 8d455f5055..627c007fa1 100644 --- a/showcase/src/app/demo/index.ts +++ b/showcase/src/app/demo/index.ts @@ -13,6 +13,7 @@ export * from "./header"; export * from "./keyboard-directives"; export * from "./language-selector"; export * from "./logout"; +export * from "./minimap"; export * from "./menu"; export * from "./pagination"; export * from "./pretty-print"; diff --git a/showcase/src/app/demo/minimap/demo-minimap.component.html b/showcase/src/app/demo/minimap/demo-minimap.component.html new file mode 100644 index 0000000000..0aed30d85b --- /dev/null +++ b/showcase/src/app/demo/minimap/demo-minimap.component.html @@ -0,0 +1,18 @@ +

SHOWCASE.DEMO.MINIMAP.TITLE

+
+ + + + +
+ +
+ + + + +
+ + diff --git a/showcase/src/app/demo/minimap/demo-minimap.component.scss b/showcase/src/app/demo/minimap/demo-minimap.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/showcase/src/app/demo/minimap/demo-minimap.component.ts b/showcase/src/app/demo/minimap/demo-minimap.component.ts new file mode 100644 index 0000000000..b156607d8b --- /dev/null +++ b/showcase/src/app/demo/minimap/demo-minimap.component.ts @@ -0,0 +1,48 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { ReferenceLink } from "../../shared/reference-block"; +import { ItemVisibility, StarkMinimapItemProperties } from "@nationalbankbelgium/stark-ui"; + +@Component({ + selector: "demo-minimap", + templateUrl: "./demo-minimap.component.html", + styleUrls: ["./demo-minimap.component.scss"] +}) +export class DemoMinimapComponent implements OnInit { + public items: StarkMinimapItemProperties[]; + public visibleItems: string[]; + public referenceList: ReferenceLink[]; + + public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {} + + /** + * Component lifecycle hook + */ + public ngOnInit(): void { + this.items = [ + { name: "first", label: "first" }, + { name: "second", label: "second" }, + { name: "third", label: "third" }, + { name: "fourth", label: "fourth" } + ]; + + this.visibleItems = [this.items[0].name, this.items[1].name, this.items[2].name, this.items[3].name]; + + this.referenceList = [ + { + label: "Stark Minimap component", + url: "" + } + ]; + } + + public showHideItem(itemToHandle: ItemVisibility): void { + const index: number = this.visibleItems.indexOf(itemToHandle.item.name); + + if (!itemToHandle.isVisible) { + this.visibleItems = [...this.visibleItems.slice(0, index), ...this.visibleItems.slice(index + 1)]; + } else { + this.visibleItems = [...this.visibleItems, itemToHandle.item.name]; + } + } +} diff --git a/showcase/src/app/demo/minimap/index.ts b/showcase/src/app/demo/minimap/index.ts new file mode 100644 index 0000000000..610cc3f921 --- /dev/null +++ b/showcase/src/app/demo/minimap/index.ts @@ -0,0 +1 @@ +export * from "./demo-minimap.component"; diff --git a/showcase/src/assets/examples/minimap/compact.html b/showcase/src/assets/examples/minimap/compact.html new file mode 100644 index 0000000000..600d5eb153 --- /dev/null +++ b/showcase/src/assets/examples/minimap/compact.html @@ -0,0 +1,2 @@ + + diff --git a/showcase/src/assets/examples/minimap/compact.ts b/showcase/src/assets/examples/minimap/compact.ts new file mode 100644 index 0000000000..c8199e2e3d --- /dev/null +++ b/showcase/src/assets/examples/minimap/compact.ts @@ -0,0 +1,44 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { ItemVisibility, StarkMinimapItemProperties } from "@nationalbankbelgium/stark-ui"; + +@Component({ + selector: "demo-minimap", + templateUrl: "./compact.html" +}) +export class DemoCompactMinimapComponent implements OnInit { + public items: StarkMinimapItemProperties[]; + public visibleItems: string[]; + + public constructor() { + // + } + + /** + * Component lifecycle hook + */ + public ngOnInit(): void { + this.items = [ + { name: "first", label: "first" }, + { name: "second", label: "second" }, + { name: "third", label: "third" }, + { name: "fourth", label: "fourth" } + ]; + + this.visibleItems = [this.items[0].name, this.items[1].name, this.items[2].name, this.items[3].name]; + } + + /** + * This method is used here to retrieve from the list of visible items the items that should be hidden. + * @param itemToHandle - the item to analyse + */ + public showHideItem(itemToHandle: ItemVisibility): void { + const index: number = this.visibleItems.indexOf(itemToHandle.item.name); + + if (!itemToHandle.isVisible) { + this.visibleItems = [...this.visibleItems.slice(0, index), ...this.visibleItems.slice(index + 1)]; + } else { + this.visibleItems = [...this.visibleItems, itemToHandle.item.name]; + } + } +} diff --git a/showcase/src/assets/examples/minimap/full.html b/showcase/src/assets/examples/minimap/full.html new file mode 100644 index 0000000000..30ec2c8e6e --- /dev/null +++ b/showcase/src/assets/examples/minimap/full.html @@ -0,0 +1,2 @@ + + diff --git a/showcase/src/assets/examples/minimap/full.ts b/showcase/src/assets/examples/minimap/full.ts new file mode 100644 index 0000000000..c88b2aa813 --- /dev/null +++ b/showcase/src/assets/examples/minimap/full.ts @@ -0,0 +1,40 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { ItemVisibility, StarkMinimapItemProperties } from "@nationalbankbelgium/stark-ui"; + +@Component({ + selector: "demo-minimap", + templateUrl: "./full.html" +}) +export class DemoFullMinimapComponent implements OnInit { + public items: StarkMinimapItemProperties[]; + public visibleItems: string[]; + + public constructor() { + // + } + + /** + * Component lifecycle hook + */ + public ngOnInit(): void { + this.items = [ + { name: "first", label: "first" }, + { name: "second", label: "second" }, + { name: "third", label: "third" }, + { name: "fourth", label: "fourth" } + ]; + + this.visibleItems = [this.items[0].name, this.items[1].name, this.items[2].name, this.items[3].name]; + } + + public showHideItem(itemToHandle: ItemVisibility): void { + const index: number = this.visibleItems.indexOf(itemToHandle.item.name); + + if (!itemToHandle.isVisible) { + this.visibleItems = [...this.visibleItems.slice(0, index), ...this.visibleItems.slice(index + 1)]; + } else { + this.visibleItems = [...this.visibleItems, itemToHandle.item.name]; + } + } +} diff --git a/showcase/src/assets/translations/en.json b/showcase/src/assets/translations/en.json index 473a7bcec8..4e309d44a9 100644 --- a/showcase/src/assets/translations/en.json +++ b/showcase/src/assets/translations/en.json @@ -154,6 +154,11 @@ "SECTIONS": "Menu with sections", "TITLE": "Stark menu" }, + "MINIMAP": { + "TITLE": "Stark Minimap", + "FULL": "Minimap (classic version)", + "COMPACT": "Minimap (compact version)" + }, "PAGINATION": { "ADVANCED_CONFIGURATION": "Pagination with advanced configuration", "COMPACT_CONFIGURATION": "Pagination in compact mode", diff --git a/showcase/src/assets/translations/fr.json b/showcase/src/assets/translations/fr.json index e7502efb9f..66fd345828 100644 --- a/showcase/src/assets/translations/fr.json +++ b/showcase/src/assets/translations/fr.json @@ -154,6 +154,11 @@ "SECTIONS": "Menu avec sections", "TITLE": "Stark menu" }, + "MINIMAP": { + "TITLE": "Stark Minimap", + "FULL": "Minimap (version classic)", + "COMPACT": "Minimap (version compacte)" + }, "PAGINATION": { "ADVANCED_CONFIGURATION": "Pagination avec configuration avancée", "COMPACT_CONFIGURATION": "Pagination en mode compact", diff --git a/showcase/src/assets/translations/nl.json b/showcase/src/assets/translations/nl.json index 101c36056a..2b67d433b3 100644 --- a/showcase/src/assets/translations/nl.json +++ b/showcase/src/assets/translations/nl.json @@ -149,6 +149,11 @@ "LOGOUT_CUSTOM_ICON": "Logout-knop - Aangepast pictogram", "TITLE": "Stark logout" }, + "MINIMAP": { + "TITLE": "Stark Minimap", + "FULL": "Minimap (klassieke versie)", + "COMPACT": "Minimap (compacte version)" + }, "MENU": { "SIMPLE": "Simple menu", "SECTIONS": "Menu with sections", diff --git a/showcase/src/styles/_stark-styles.scss b/showcase/src/styles/_stark-styles.scss index a95594de37..3b18521a11 100644 --- a/showcase/src/styles/_stark-styles.scss +++ b/showcase/src/styles/_stark-styles.scss @@ -20,6 +20,8 @@ IMPORTANT: Stark styles are provided as SCSS styles so they should be imported i @import "~@nationalbankbelgium/stark-ui/src/modules/collapsible/components/collapsible.component"; @import "~@nationalbankbelgium/stark-ui/src/modules/date-range-picker/components/date-range-picker.component"; @import "~@nationalbankbelgium/stark-ui/src/modules/language-selector/components/language-selector.component"; +@import "~@nationalbankbelgium/stark-ui/src/modules/minimap/components/minimap.component"; +@import "~@nationalbankbelgium/stark-ui/src/modules/minimap/components/minimap-theme"; @import "~@nationalbankbelgium/stark-ui/src/modules/slider/components/slider-theme"; @import "~@nationalbankbelgium/stark-ui/src/modules/pagination/components/pagination.component"; @import "~@nationalbankbelgium/stark-ui/src/modules/pagination/components/pagination-theme";