diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a9e1c0d..803a844bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# v14.5.0 (2023-03-02) +* **grid** add card view support + # v14.4.1 (2023-02-15) * **dateformat** use luxon for absolute time if enabled diff --git a/package-lock.json b/package-lock.json index aa766d831..6c79bda37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "angular-components", - "version": "14.4.1", + "version": "14.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "angular-components", - "version": "14.4.1", + "version": "14.5.0", "license": "MIT", "dependencies": { "@angular/animations": "14.2.12", diff --git a/package.json b/package.json index fa5eb094c..da07e1fa5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-components", - "version": "14.4.1", + "version": "14.5.0", "author": { "name": "UiPath Inc", "url": "https://uipath.com" diff --git a/projects/angular/components/ui-grid/src/_ui-grid.theme.scss b/projects/angular/components/ui-grid/src/_ui-grid.theme.scss index 8787b1a6d..33eeec252 100644 --- a/projects/angular/components/ui-grid/src/_ui-grid.theme.scss +++ b/projects/angular/components/ui-grid/src/_ui-grid.theme.scss @@ -52,7 +52,8 @@ $ui-grid-opacity-transition: opacity $ui-grid-default-transition; } &.ui-grid-state-loading { - .ui-grid-row { + .ui-grid-row, + .ui-grid-card-wrapper { pointer-events: none; opacity: 0.5; } @@ -150,6 +151,10 @@ $ui-grid-opacity-transition: opacity $ui-grid-default-transition; border-bottom-color: $ui-grid-border-color; transition: $ui-grid-opacity-transition; } + + &-card-wrapper { + transition: $ui-grid-opacity-transition; + } } .ui-grid-row { diff --git a/projects/angular/components/ui-grid/src/body/ui-grid-row-card-view.directive.ts b/projects/angular/components/ui-grid/src/body/ui-grid-row-card-view.directive.ts new file mode 100644 index 000000000..05f51ffa3 --- /dev/null +++ b/projects/angular/components/ui-grid/src/body/ui-grid-row-card-view.directive.ts @@ -0,0 +1,18 @@ +import { + ContentChild, + Directive, TemplateRef, +} from '@angular/core'; + +export interface IGridRowCardViewContext { + index: number; + last: boolean; + data: T; +} + +@Directive({ selector: '[uiGridRowCardView], ui-grid-row-card-view' }) +export class UiGridRowCardViewDirective { + @ContentChild(TemplateRef, { + static: true, + }) + html?: TemplateRef>; +} diff --git a/projects/angular/components/ui-grid/src/public_api.ts b/projects/angular/components/ui-grid/src/public_api.ts index 4467b3987..1ff26f8b4 100644 --- a/projects/angular/components/ui-grid/src/public_api.ts +++ b/projects/angular/components/ui-grid/src/public_api.ts @@ -15,6 +15,7 @@ export { UiGridColumnDirective } from './body/ui-grid-column.directive'; export { UiGridExpandedRowDirective } from './body/ui-grid-expanded-row.directive'; export { UiGridRowActionDirective } from './body/ui-grid-row-action.directive'; export { UiGridRowConfigDirective } from './body/ui-grid-row-config.directive'; +export { UiGridRowCardViewDirective } from './body/ui-grid-row-card-view.directive'; export { UiGridLoadingDirective } from './body/ui-grid-loading.directive'; export { UiGridNoContentDirective } from './body/ui-grid-no-content.directive'; export { UiGridSearchComponent } from './components/ui-grid-search/ui-grid-search.component'; diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.html b/projects/angular/components/ui-grid/src/ui-grid.component.html index 18ec0f89d..7d6a05547 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.html +++ b/projects/angular/components/ui-grid/src/ui-grid.component.html @@ -183,7 +183,7 @@ -
@@ -232,31 +232,65 @@ [itemSize]="rowSize" uiVirtualScrollViewportResize class="ui-grid-viewport"> - +
+ + + +
+
+ + - + + - +
+ + + + +
+
+ + - + + @@ -401,6 +435,58 @@ [matTooltipDisabled]="resizeManager.isResizing">{{ dataManager.getProperty(row, property) }}

+ +
+ + + +
+
+ + +
+ +
+
{{column.directive.title}}
+
+ {{ column.directive.title ?? column.directive.property }}: + +
+
+
+
+
+ diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.scss b/projects/angular/components/ui-grid/src/ui-grid.component.scss index cf6645355..c07fd2cce 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.scss +++ b/projects/angular/components/ui-grid/src/ui-grid.component.scss @@ -515,6 +515,34 @@ ui-grid { .ui-grid-header-cell-sortable { cursor: pointer; } + + .ui-grid-cards-container { + margin: 16px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + + @supports (display: grid) { + display: grid; + grid-column-gap: 12px; + grid-row-gap: 16px; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + } + } + + .ui-grid-card-default { + border-radius: 5px; + background: #ffffff; + border: 1px solid #cfd8dd; + color: #273139; + padding: 16px; + } + + .ui-grid-card-default-cell-content { + display: flex; + align-items: center; + gap: 8px; + } } } } diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts b/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts index 870c42ab1..75ee56dba 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts @@ -4178,4 +4178,153 @@ describe('Component: UiGrid', () => { expect(expandedRows.length).toBe(fixture.componentInstance.expandedEntries.length); }); }); + + describe('Scenatio: card view', () => { + + describe('Behavior: default card view', () => { + + @Component({ + template: ` + + + + + + + + + `, + }) + class TextFixtureGridCardViewComponent { + @ViewChild(UiGridComponent, { + static: true, + }) + grid!: UiGridComponent; + + data: ITestEntity[] = []; + } + + let fixture: ComponentFixture; + let component: TextFixtureGridCardViewComponent; + let data: ITestEntity[]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + UiGridModule, + NoopAnimationsModule, + ], + declarations: [TextFixtureGridCardViewComponent], + }); + + fixture = TestBed.createComponent(TextFixtureGridCardViewComponent); + component = fixture.componentInstance; + data = generateListFactory(generateEntity)(6); + component.data = data; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should render default card', () => { + + const cardContainer = fixture.debugElement.query(By.css('.ui-grid-card-container')); + expect(cardContainer).toBeDefined(); + + }); + + it('should render properties defined in the column section', () => { + + const cells = fixture.debugElement.queryAll(By.css('.ui-grid-cards-container .ui-grid-card-default-cell-content')); + + expect((cells[0].nativeElement as HTMLElement).innerHTML).toContain('Number Header'); + expect((cells[1].nativeElement as HTMLElement).innerHTML).toContain('String Header'); + + }); + + }); + + describe('Behavior: card view with a template', () => { + @Component({ + template: ` + + + +
+

{{entry.myNumber}}

+

{{entry.myBool}}

+
+
+
+
+ `, + }) + class TextFixtureGridCardViewComponent { + @ViewChild(UiGridComponent, { + static: true, + }) + grid!: UiGridComponent; + + data: ITestEntity[] = []; + } + + let fixture: ComponentFixture; + let component: TextFixtureGridCardViewComponent; + let data: ITestEntity[]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + UiGridModule, + NoopAnimationsModule, + ], + declarations: [TextFixtureGridCardViewComponent], + }); + + fixture = TestBed.createComponent(TextFixtureGridCardViewComponent); + component = fixture.componentInstance; + data = generateListFactory(generateEntity)(6); + component.data = data; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should render provided card template', () => { + + const cardContainer = fixture.debugElement.query(By.css('.expanded-row')); + + console.log(cardContainer); + expect(cardContainer).toBeDefined(); + expect(cardContainer.nativeElement.querySelector('[data-property="myNumber"]')).toBeDefined(); + expect(cardContainer.nativeElement.querySelector('[data-property="myBool"]')).toBeDefined(); + + }); + + it('should not render any default card view', () => { + const cardContainer = fixture.debugElement.query(By.css('.ui-grid-card-container')); + expect(cardContainer).toBeNull(); + }); + }); + + }); + }); diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.ts b/projects/angular/components/ui-grid/src/ui-grid.component.ts index 8d454580b..e396c7673 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.component.ts @@ -66,6 +66,7 @@ import { UiGridLoadingDirective } from './body/ui-grid-loading.directive'; import { UiGridNoContentDirective } from './body/ui-grid-no-content.directive'; import { UiGridRowActionDirective } from './body/ui-grid-row-action.directive'; import { UiGridRowConfigDirective } from './body/ui-grid-row-config.directive'; +import { UiGridRowCardViewDirective } from './body/ui-grid-row-card-view.directive'; import { UiGridSearchFilterDirective } from './filters/ui-grid-search-filter.directive'; import { UiGridFooterDirective } from './footer/ui-grid-footer.directive'; import { UiGridHeaderDirective } from './header/ui-grid-header.directive'; @@ -375,6 +376,13 @@ export class UiGridComponent extends ResizableGrid this.filterManager.updateCustomFilters(customValue); } + /** + * Configure if Card view should be used + * + */ + @Input() + useCardView = false; + /** * Emits an event with the sort model when a column sort changes. * @@ -496,6 +504,16 @@ export class UiGridComponent extends ResizableGrid static: true, }) loadingState?: UiGridLoadingDirective; + + /** + * Custom card view template reference. + * + * @ignore + */ + @ContentChild(UiGridRowCardViewDirective, { + static: true, + }) + cardTemplate?: UiGridRowCardViewDirective; /** * Reference to the grid action buttons container * diff --git a/projects/angular/components/ui-grid/src/ui-grid.module.ts b/projects/angular/components/ui-grid/src/ui-grid.module.ts index ba4ad8827..9497c30ce 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.module.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.module.ts @@ -21,6 +21,7 @@ import { UiGridLoadingDirective } from './body/ui-grid-loading.directive'; import { UiGridNoContentDirective } from './body/ui-grid-no-content.directive'; import { UiGridRowActionDirective } from './body/ui-grid-row-action.directive'; import { UiGridRowConfigDirective } from './body/ui-grid-row-config.directive'; +import { UiGridRowCardViewDirective } from './body/ui-grid-row-card-view.directive'; import { UiGridCustomPaginatorModule } from './components/ui-grid-custom-paginator/ui-grid-custom-paginator.module'; import { UiGridSearchModule } from './components/ui-grid-search/ui-grid-search.module'; import { UiGridToggleColumnsModule } from './components/ui-grid-toggle-columns/ui-grid-toggle-columns.module'; @@ -65,6 +66,7 @@ import { UiGridComponent } from './ui-grid.component'; UiGridExpandedRowDirective, UiGridNoContentDirective, UiGridLoadingDirective, + UiGridRowCardViewDirective, ], exports: [ UiGridComponent, @@ -79,6 +81,7 @@ import { UiGridComponent } from './ui-grid.component'; UiGridExpandedRowDirective, UiGridNoContentDirective, UiGridLoadingDirective, + UiGridRowCardViewDirective, ], }) export class UiGridModule { } diff --git a/projects/angular/package.json b/projects/angular/package.json index 32012fbd4..c6af97ce6 100644 --- a/projects/angular/package.json +++ b/projects/angular/package.json @@ -1,6 +1,6 @@ { "name": "@uipath/angular", - "version": "14.4.1", + "version": "14.5.0", "license": "MIT", "author": { "name": "UiPath Inc", diff --git a/projects/playground/src/app/pages/grid/component/grid.component.html b/projects/playground/src/app/pages/grid/component/grid.component.html index eff58d178..6e87e5852 100644 --- a/projects/playground/src/app/pages/grid/component/grid.component.html +++ b/projects/playground/src/app/pages/grid/component/grid.component.html @@ -13,6 +13,7 @@ [showPaintTime]="inputs.showPaintTime" [showHeaderRow]="inputs.showHeaderRow" [expandedEntries]="editedEntity" + [useCardView]="inputs.useCardView" [expandMode]="'preserve'" [customFilterValue]="inputs.customFilter ? [{property: 'parity', method: 'eq', value: 'odd'}] : []"> @@ -106,4 +107,29 @@

Expanded row with ID: {{ entry.id }}

+ + + +
+

{{data.name}}

+

parity: {{data.parity}}

+ + + + + + +
+ +
+
diff --git a/projects/playground/src/app/pages/grid/component/grid.component.scss b/projects/playground/src/app/pages/grid/component/grid.component.scss index 056b92dbd..2bc27251a 100644 --- a/projects/playground/src/app/pages/grid/component/grid.component.scss +++ b/projects/playground/src/app/pages/grid/component/grid.component.scss @@ -4,3 +4,12 @@ justify-content: center; align-items: center; } + +.card { + background: #ffffff; + border: 1px solid #cfd8dd; + color: #273139; + padding: 16px; + text-align: center; + border-radius: 5px; +} diff --git a/projects/playground/src/app/pages/grid/grid.models.ts b/projects/playground/src/app/pages/grid/grid.models.ts index ad4ccf8cd..43c7f3ea9 100644 --- a/projects/playground/src/app/pages/grid/grid.models.ts +++ b/projects/playground/src/app/pages/grid/grid.models.ts @@ -28,4 +28,5 @@ export interface IInputs { showPaintTime: boolean; showHeaderRow: boolean; customFilter: boolean; + useCardView: boolean; } diff --git a/projects/playground/src/app/pages/grid/grid.page.ts b/projects/playground/src/app/pages/grid/grid.page.ts index 7f3e2251f..c3d0faf3a 100644 --- a/projects/playground/src/app/pages/grid/grid.page.ts +++ b/projects/playground/src/app/pages/grid/grid.page.ts @@ -53,6 +53,7 @@ export class GridPageComponent implements AfterViewInit { 'showPaintTime', 'showHeaderRow', 'customFilter', + 'useCardView', ]; buttonKeys = [ @@ -103,6 +104,7 @@ export class GridPageComponent implements AfterViewInit { showPaintTime: [false], showHeaderRow: [true], customFilter: [false], + useCardView: [true], }), header: this._fb.group({ searchable: [true],