Skip to content

Commit

Permalink
feat(design): implement article encapsulated mixin in DaffTreeCompone…
Browse files Browse the repository at this point in the history
…nt and add docs to design-land (#2771)
  • Loading branch information
xelaint committed Mar 26, 2024
1 parent a7bd640 commit baa150e
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 8 deletions.
1 change: 1 addition & 0 deletions apps/design-land/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const appRoutes: Routes = [
{ path: 'sidebar', loadChildren: () => import('./sidebar/sidebar.module').then(m => m.DesignLandSidebarModule) },
{ path: 'radio', loadChildren: () => import('./radio/radio.module').then(m => m.DesignLandRadioModule) },
{ path: 'toast', loadChildren: () => import('./toast/toast.module').then(m => m.DesignLandToastModule) },
{ path: 'tree', loadChildren: () => import('./tree/tree.module').then(m => m.DesignLandTreeModule) },
{ path: 'typography', loadChildren: () => import('./typography/typography.module').then(m => m.DesignLandTypographyModule) },
{ path: 'variables', loadChildren: () => import('./foundations/variables/variables.module').then(m => m.DesignLandVariablesModule) },
],
Expand Down
2 changes: 2 additions & 0 deletions apps/design-land/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { QUANTITY_FIELD_EXAMPLES } from '@daffodil/design/quantity-field/example
import { RADIO_EXAMPLES } from '@daffodil/design/radio/examples';
import { SIDEBAR_EXAMPLES } from '@daffodil/design/sidebar/examples';
import { TOAST_EXAMPLES } from '@daffodil/design/toast/examples';
import { TREE_EXAMPLES } from '@daffodil/design/tree/examples';

import { createCustomElementFromExample } from './core/elements/create-element-from-example';

Expand Down Expand Up @@ -62,6 +63,7 @@ export class DesignLandAppComponent {
...INPUT_EXAMPLES,
...SIDEBAR_EXAMPLES,
...TOAST_EXAMPLES,
...TREE_EXAMPLES,
].map((componentExample) => createCustomElementFromExample(componentExample, injector))
.map((customElement) => {
// Register the custom element with the browser.
Expand Down
21 changes: 21 additions & 0 deletions apps/design-land/src/app/tree/tree-routing-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import {
Routes,
RouterModule,
} from '@angular/router';

import { DesignLandTreeComponent } from './tree.component';

export const treeRoutes: Routes = [
{ path: '', component: DesignLandTreeComponent },
];

@NgModule({
imports: [
RouterModule.forChild(treeRoutes),
],
exports: [
RouterModule,
],
})
export class DesignLandTreeRoutingModule {}
23 changes: 23 additions & 0 deletions apps/design-land/src/app/tree/tree.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<h1 daffArticleTitle>Tree</h1>
<p daffArticleLead>Trees are used to visualize hierarchial information. They are often used to display navigational structures like nested lists of links.</p>

<h2>Overview</h2>
<p>The <code>DaffTreeComponent</code> renders a tree structure. Typically, this is a structure of <code>&lt;a&gt;</code> and <code>&lt;button&gt;</code> elements that allow users to either navigate to a page, or explore the tree to find an item inside the tree that they want to navigate to.</p>

<p>Instead of defining a recursive tree structure of components, which is often prohibitively slow when rendering large trees, the <code>DaffTreeComponent</code> renders a flattened tree, using padding to indicate the nesting level of the tree elements.</p>

<p>Generally, tree usage consists of taking existing tree data, converting it to the <code>DaffTreeData</code> format, setting the <code>tree</code> input on the <code>DaffTreeComponent</code>, and providing templates for the cases where the tree element has children or not.</p>

<h2>Features</h2>
<p>The <code>DaffTreeComponent</code> controls the rendering of the structure of the tree and provides template slots so that you can control the ultimate UI rendered for each node.</p>

<p>Currently, we support two kind of templates: <code>daffTreeItemWithChildrenTpl</code> and <code>daffTreeItemTpl</code>. These templates allow you to control the content of each tree node. In the case of <code>daffTreeItemWithChildrenTpl</code>, a <code>click</code> handler will be automatically applied (along with an icon indicating the expanded state) to the template to allow users to automatically open and close the node.</p>

<h2>Usage</h2>

<h3>Basic Tree</h3>
<design-land-example-viewer-container example="basic-tree">
</design-land-example-viewer-container>

<h2>Accessibility</h2>
<p>The <code>DaffTreeComponent</code> follows the specification for a <a href="https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/" target="_blank">disclosure navigation menu</a> instead of a <a href="https://www.w3.org/WAI/ARIA/apg/patterns/treeview/" target="_blank">tree view</a>.</p>
Empty file.
29 changes: 29 additions & 0 deletions apps/design-land/src/app/tree/tree.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
ComponentFixture,
TestBed,
waitForAsync,
} from '@angular/core/testing';

import { DesignLandTreeComponent } from './tree.component';

describe('DesignLandTreeComponent', () => {
let component: DesignLandTreeComponent;
let fixture: ComponentFixture<DesignLandTreeComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ DesignLandTreeComponent ],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(DesignLandTreeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
8 changes: 8 additions & 0 deletions apps/design-land/src/app/tree/tree.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'design-land-tree',
templateUrl: './tree.component.html',
styleUrls: ['./tree.component.scss'],
})
export class DesignLandTreeComponent {}
28 changes: 28 additions & 0 deletions apps/design-land/src/app/tree/tree.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { DaffArticleModule } from '@daffodil/design/article';
import { DaffTreeModule } from '@daffodil/design/tree';

import { DesignLandTreeRoutingModule } from './tree-routing-module';
import { DesignLandTreeComponent } from './tree.component';
import { DesignLandArticleEncapsulatedModule } from '../core/article-encapsulated/article-encapsulated.module';
import { DesignLandExampleViewerModule } from '../core/code-preview/container/example-viewer.module';

@NgModule({
declarations: [
DesignLandTreeComponent,
],
imports: [
CommonModule,
RouterModule,
DesignLandTreeRoutingModule,
DesignLandExampleViewerModule,
DesignLandArticleEncapsulatedModule,

DaffArticleModule,
DaffTreeModule,
],
})
export class DesignLandTreeModule {}
7 changes: 7 additions & 0 deletions apps/design-land/src/assets/nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,13 @@
"id": "toast",
"items": [],
"data": {}
},
{
"title": "Tree",
"url": "tree",
"id": "tree",
"items": [],
"data": {}
}
],
"data": {}
Expand Down
4 changes: 1 addition & 3 deletions libs/design/tree/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Tree

Trees are used to visualize hierarchial information. They are often used to display navigational structures like nested lists of links.

## Overview
Expand All @@ -11,10 +10,9 @@ Instead of defining a recursive tree structure of components, which is often pro
Generally, tree usage consists of taking existing tree data, converting it to the `DaffTreeData` format, setting the `tree` input on the `DaffTreeComponent`, and providing templates for the cases where the tree element has children or not.

## Features

The `DaffTreeComponent` controls the rendering of the structure of the tree and provides template slots so that you can control the ultimate UI rendered for each node.

Currently, we support two kind of templates `daffTreeItemWithChildrenTpl` and `daffTreeItemTpl`. These templates allow you to control the content of each tree node. In the case of `daffTreeItemWithChildrenTpl` a `click` handler will be automatically applied (along with an icon indicating the expanded state) to the template to allow users to automatically open and close the node.
Currently, we support two kind of templates: `daffTreeItemWithChildrenTpl` and `daffTreeItemTpl`. These templates allow you to control the content of each tree node. In the case of `daffTreeItemWithChildrenTpl`, a `click` handler will be automatically applied (along with an icon indicating the expanded state) to the template to allow users to automatically open and close the node.

```html
<ng-template #daffTreeItemWithChildrenTpl let-node>
Expand Down
4 changes: 2 additions & 2 deletions libs/design/tree/src/tree/specs/simple.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ describe('@daffodil/design/tree - DaffTreeComponent | Simple', () => {
});

it('should render nothing', () => {
expect(fixture.debugElement.nativeElement.innerHTML).toContain(`<ul daff-tree="" class="daff-tree"`);
expect(fixture.debugElement.nativeElement.innerHTML).toContain(`<ul daff-tree="" class="daff-ae daff-tree"`);
});

it('should render nothing when data is provided with no templates', () => {
wrapper.data = { title: '', url: '', id: '', items: [], data: {}};
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML).toContain(`<ul daff-tree="" class="daff-tree"`);
expect(fixture.debugElement.nativeElement.innerHTML).toContain(`<ul daff-tree="" class="daff-ae daff-tree"`);
});
});
22 changes: 19 additions & 3 deletions libs/design/tree/src/tree/tree.component.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { Location } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
ContentChild,
ElementRef,
HostBinding,
Input,
OnInit,
Renderer2,
TemplateRef,
ViewEncapsulation,
} from '@angular/core';

import { daffArticleEncapsulatedMixin } from '@daffodil/design';

import { DaffTreeNotifierService } from './tree-notifier.service';
import { DaffTreeData } from '../interfaces/tree-data';
import { DaffTreeUi } from '../interfaces/tree-ui';
Expand All @@ -19,6 +22,15 @@ import {
} from '../utils/flatten-tree';
import { hydrateTree } from '../utils/hydrate-tree';

/**
* An _elementRef and an instance of renderer2 are needed for the list mixins
*/
class DaffTreeBase {
constructor(public _elementRef: ElementRef, public _renderer: Renderer2) {}
}

const _daffTreeBase = daffArticleEncapsulatedMixin((DaffTreeBase));

/**
* The `DaffTreeComponent` allows you to render tree structures as interactable ui.
*
Expand Down Expand Up @@ -50,7 +62,7 @@ import { hydrateTree } from '../utils/hydrate-tree';
DaffTreeNotifierService,
],
})
export class DaffTreeComponent implements OnInit {
export class DaffTreeComponent extends _daffTreeBase implements OnInit {

/**
* The css class of the daff-tree.
Expand Down Expand Up @@ -111,7 +123,11 @@ export class DaffTreeComponent implements OnInit {

constructor(
private notifier: DaffTreeNotifierService,
) {}
private elementRef: ElementRef,
private renderer: Renderer2,
) {
super(elementRef, renderer);
}

/**
* The track-by function used to reduce tree-item re-renders
Expand Down

0 comments on commit baa150e

Please sign in to comment.