Browse Source

Merge pull request #24689 from abpframework/issue-24684

Angular Aria Implementation for ABP Packages - Issue 24684
pull/24758/head
oykuermann 1 week ago
committed by GitHub
parent
commit
c59db2f5cd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      npm/ng-packs/packages/feature-management/package.json
  2. 42
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html
  3. 11
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts
  4. 3
      npm/ng-packs/packages/identity/package.json
  5. 72
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  6. 12
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  7. 3
      npm/ng-packs/packages/permission-management/package.json
  8. 123
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html
  9. 22
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  10. 2
      npm/ng-packs/packages/setting-management/package.json

3
npm/ng-packs/packages/feature-management/package.json

@ -10,6 +10,9 @@
"@abp/ng.theme.shared": "~10.1.0-rc.2", "@abp/ng.theme.shared": "~10.1.0-rc.2",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": {
"@angular/aria": "~21.0.0"
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }

42
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html

@ -12,18 +12,26 @@
<ng-template #abpBody> <ng-template #abpBody>
<div class="row"> <div class="row">
@if (groups.length) { @if (groups.length) {
<div class="col-md-4"> <div ngTabs orientation="vertical" class="row">
<ul <div class="col-md-4">
ngbNav <div ngTabList orientation="vertical" selectionMode="follow" [(selectedTab)]="selectedGroupDisplayName" class="nav nav-pills flex-column">
#nav="ngbNav" @for (group of groups; track group.name) {
[(activeId)]="selectedGroupDisplayName" <button ngTab #tab="ngTab" type="button" [value]="group.displayName" class="nav-link text-start" [class.active]="tab.selected()">
class="nav-pills" {{ group.displayName }}
orientation="vertical" </button>
> }
</div>
</div>
<ng-template #descTmp let-description>
@if (description) {
<small class="d-block form-text text-muted">{{ description }}</small>
}
</ng-template>
<div class="col-md-8">
@for (group of groups; track group.name) { @for (group of groups; track group.name) {
<li [ngbNavItem]="group.displayName"> <div ngTabPanel [value]="group.displayName">
<a ngbNavLink>{{ group.displayName }}</a> <ng-template ngTabContent>
<ng-template ngbNavContent>
<h4>{{ selectedGroupDisplayName }}</h4> <h4>{{ selectedGroupDisplayName }}</h4>
<hr class="mt-2 mb-3" /> <hr class="mt-2 mb-3" />
@ -128,18 +136,10 @@
</div> </div>
} }
</ng-template> </ng-template>
</li> </div>
} }
</ul> </div>
</div> </div>
<ng-template #descTmp let-description>
@if (description) {
<small class="d-block form-text text-muted">{{ description }}</small>
}
</ng-template>
<div class="col-md-8"><div class="py-0" [ngbNavOutlet]="nav"></div></div>
} }
@if (!groups.length) { @if (!groups.length) {

11
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts

@ -1,5 +1,5 @@
import { Component, EventEmitter, Input, Output, inject, DOCUMENT } from '@angular/core'; import { Component, EventEmitter, Input, Output, inject, DOCUMENT } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common'; import { NgTemplateOutlet, NgStyle } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { ConfigStateService, LocalizationPipe, TrackByService } from '@abp/ng.core'; import { ConfigStateService, LocalizationPipe, TrackByService } from '@abp/ng.core';
import { import {
@ -17,7 +17,7 @@ import {
ModalComponent, ModalComponent,
ToasterService, ToasterService,
} from '@abp/ng.theme.shared'; } from '@abp/ng.theme.shared';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { Tabs, TabList, Tab, TabPanel, TabContent } from '@angular/aria/tabs';
import { finalize } from 'rxjs/operators'; import { finalize } from 'rxjs/operators';
import { FreeTextInputDirective } from '../../directives'; import { FreeTextInputDirective } from '../../directives';
import { FeatureManagement } from '../../models'; import { FeatureManagement } from '../../models';
@ -36,11 +36,16 @@ const DEFAULT_PROVIDER_NAME = 'D';
exportAs: 'abpFeatureManagement', exportAs: 'abpFeatureManagement',
imports: [ imports: [
NgTemplateOutlet, NgTemplateOutlet,
NgStyle,
ButtonComponent, ButtonComponent,
ModalComponent, ModalComponent,
LocalizationPipe, LocalizationPipe,
FormsModule, FormsModule,
NgbNavModule, Tabs,
TabList,
Tab,
TabPanel,
TabContent,
FreeTextInputDirective, FreeTextInputDirective,
ModalCloseDirective, ModalCloseDirective,
], ],

3
npm/ng-packs/packages/identity/package.json

@ -12,6 +12,9 @@
"@abp/ng.theme.shared": "~10.1.0-rc.2", "@abp/ng.theme.shared": "~10.1.0-rc.2",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": {
"@angular/aria": "~21.0.0"
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }

72
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -28,39 +28,47 @@
<ng-template #abpBody> <ng-template #abpBody>
@if (form) { @if (form) {
<form [formGroup]="form" (ngSubmit)="save()"> <form [formGroup]="form" (ngSubmit)="save()">
<ul ngbNav #nav="ngbNav" class="nav-tabs"> <div ngTabs selectionMode="follow">
<li ngbNavItem> <div ngTabList [(selectedTab)]="selectedTab" class="nav nav-tabs">
<a ngbNavLink>{{ 'AbpIdentity::UserInformations' | abpLocalization }}</a> <button ngTab #tabInfo="ngTab" [value]="'user-info'" class="nav-link" [class.active]="tabInfo.selected()" type="button">
<ng-template ngbNavContent> {{ 'AbpIdentity::UserInformations' | abpLocalization }}
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form> </button>
</ng-template> <button ngTab #tabRoles="ngTab" [value]="'roles'" class="nav-link" [class.active]="tabRoles.selected()" type="button">
</li> {{ 'AbpIdentity::Roles' | abpLocalization }}
</button>
</div>
<li ngbNavItem> <div class="mt-2 fade-in-top">
<a ngbNavLink>{{ 'AbpIdentity::Roles' | abpLocalization }}</a> <div ngTabPanel [value]="'user-info'">
<ng-template ngbNavContent> <ng-template ngTabContent>
@for (roleGroup of roleGroups; track $index; let i = $index) { <abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
<div class="form-check mb-2"> </ng-template>
<abp-checkbox </div>
*abpReplaceableTemplate="{
inputs: { <div ngTabPanel [value]="'roles'">
checkboxId: 'roles-' + i, <ng-template ngTabContent>
label: roles[i].name, @for (roleGroup of roleGroups; track $index; let i = $index) {
formControl: roleGroup.controls[roles[i].name] <div class="form-check mb-2">
}, <abp-checkbox
componentKey: inputKey *abpReplaceableTemplate="{
}" inputs: {
[checkboxId]="'roles-' + i" checkboxId: 'roles-' + i,
[formControl]="roleGroup.controls[roles[i].name]" label: roles[i].name,
[label]="roles[i].name" formControl: roleGroup.controls[roles[i].name]
> },
</abp-checkbox> componentKey: inputKey
</div> }"
} [checkboxId]="'roles-' + i"
</ng-template> [formControl]="roleGroup.controls[roles[i].name]"
</li> [label]="roles[i].name"
</ul> >
<div class="mt-2 fade-in-top" [ngbNavOutlet]="nav"></div> </abp-checkbox>
</div>
}
</ng-template>
</div>
</div>
</div>
</form> </form>
} @else { } @else {
<div class="text-center"><i class="fa fa-pulse fa-spinner" aria-hidden="true"></i></div> <div class="text-center"><i class="fa fa-pulse fa-spinner" aria-hidden="true"></i></div>

12
npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts

@ -52,7 +52,8 @@ import {
import { finalize, switchMap, tap } from 'rxjs/operators'; import { finalize, switchMap, tap } from 'rxjs/operators';
import { eIdentityComponents } from '../../enums/components'; import { eIdentityComponents } from '../../enums/components';
import { PageComponent } from '@abp/ng.components/page'; import { PageComponent } from '@abp/ng.components/page';
import { NgbDropdownModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { Tabs, TabList, Tab, TabPanel, TabContent } from '@angular/aria/tabs';
import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxValidateCoreModule } from '@ngx-validate/core';
@Component({ @Component({
@ -70,7 +71,11 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
FormsModule, FormsModule,
PermissionManagementComponent, PermissionManagementComponent,
PageComponent, PageComponent,
NgbNavModule, Tabs,
TabList,
Tab,
TabPanel,
TabContent,
NgbDropdownModule, NgbDropdownModule,
NgxValidateCoreModule, NgxValidateCoreModule,
LocalizationPipe, LocalizationPipe,
@ -101,6 +106,8 @@ export class UsersComponent implements OnInit {
selected?: IdentityUserDto; selected?: IdentityUserDto;
selectedTab = 'user-info';
selectedUserRoles?: IdentityRoleDto[]; selectedUserRoles?: IdentityRoleDto[];
roles?: IdentityRoleDto[]; roles?: IdentityRoleDto[];
@ -159,6 +166,7 @@ export class UsersComponent implements OnInit {
} }
openModal() { openModal() {
this.selectedTab = 'user-info';
this.buildForm(); this.buildForm();
this.isModalVisible = true; this.isModalVisible = true;
} }

3
npm/ng-packs/packages/permission-management/package.json

@ -10,6 +10,9 @@
"@abp/ng.theme.shared": "~10.1.0-rc.2", "@abp/ng.theme.shared": "~10.1.0-rc.2",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": {
"@angular/aria": "~21.0.0"
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

123
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html

@ -43,18 +43,27 @@
<legend class="px-1 h5 mb-0"> <legend class="px-1 h5 mb-0">
{{ 'AbpPermissionManagement::PermissionGroup' | abpLocalization }} {{ 'AbpPermissionManagement::PermissionGroup' | abpLocalization }}
</legend> </legend>
<div class="row"> <div class="row" ngTabs orientation="vertical">
<div class="col-md-4"> <div class="col-md-4">
<div class="overflow-auto lpx-scroll-pills-container scroll-in-modal"> <div
<ul class="nav nav-pills flex-column"> class="overflow-auto lpx-scroll-pills-container scroll-in-modal"
ngTabList
orientation="vertical"
selectionMode="follow"
[selectedTab]="selectedGroup?.name"
(selectedTabChange)="onTabChange($event)"
>
<div class="nav nav-pills flex-column">
@for (group of permissionGroups(); track $index) { @for (group of permissionGroups(); track $index) {
<li class="border nav-item"> <div class="border nav-item">
@if ({ assignedCount: getAssignedCount(group.name) }; as count) { @if ({ assignedCount: getAssignedCount(group.name) }; as count) {
<a <button
class="nav-link pointer" ngTab
[class.active]="selectedGroup?.name === group?.name" [value]="group.name"
(click)="onChangeGroup(group)" class="nav-link pointer text-start w-100"
(select)="setDisabled(group.permissions)" #tab="ngTab"
[class.active]="tab.selected()"
type="button"
> >
<div [class.font-weight-bold]="count.assignedCount"> <div [class.font-weight-bold]="count.assignedCount">
{{ group?.displayName }} {{ group?.displayName }}
@ -62,59 +71,65 @@
<span>({{ count.assignedCount }})</span> <span>({{ count.assignedCount }})</span>
} }
</div> </div>
</a> </button>
} }
</li> </div>
} }
</ul> </div>
</div> </div>
</div> </div>
<div class="col-md-8 scroll-in-modal"> <div class="col-md-8 scroll-in-modal">
<div class="ps-1"> @for (group of permissionGroups(); track $index) {
@if (selectedGroupPermissions.length) { <div ngTabPanel [value]="group.name">
<div class="form-check mb-2"> <ng-template ngTabContent>
<input <div class="ps-1">
#selectAllInThisTabsRef @if (selectedGroupPermissions.length) {
type="checkbox" <div class="form-check mb-2">
id="select-all-in-this-tabs" <input
name="select-all-in-this-tabs" #selectAllInThisTabsRef
class="form-check-input" type="checkbox"
[(ngModel)]="selectThisTab" id="select-all-in-this-tabs"
[disabled]="disableSelectAllTab" name="select-all-in-this-tabs"
(click)="onClickSelectThisTab()" class="form-check-input"
/> [(ngModel)]="selectThisTab"
<label class="form-check-label" for="select-all-in-this-tabs">{{ [disabled]="disableSelectAllTab"
'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization (click)="onClickSelectThisTab()"
}}</label> />
</div> <label class="form-check-label" for="select-all-in-this-tabs">{{
<hr class="my-2" /> 'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization
@for (permission of selectedGroupPermissions; track $index; let i = $index) { }}</label>
<div [ngStyle]="permission.style" class="form-check mb-2"> </div>
<input <hr class="my-2" />
#permissionCheckbox @for (permission of selectedGroupPermissions; track $index; let i = $index) {
type="checkbox" <div [ngStyle]="permission.style" class="form-check mb-2">
[checked]="getChecked(permission.name)" <input
[value]="getChecked(permission.name)" #permissionCheckbox
[attr.id]="permission.name" type="checkbox"
class="form-check-input" [checked]="getChecked(permission.name)"
[disabled]="isGrantedByOtherProviderName(permission.grantedProviders)" [value]="getChecked(permission.name)"
(click)="onClickCheckbox(permission, permissionCheckbox.value)" [attr.id]="permission.name"
/> class="form-check-input"
<label class="form-check-label" [attr.for]="permission.name" [disabled]="isGrantedByOtherProviderName(permission.grantedProviders)"
>{{ permission.displayName }} (click)="onClickCheckbox(permission, permissionCheckbox.value)"
@if (!hideBadges) { />
@for (provider of permission.grantedProviders; track $index) { <label class="form-check-label" [attr.for]="permission.name"
<span class="badge bg-primary text-dark" >{{ permission.displayName }}
>{{ provider.providerName }}: {{ provider.providerKey }}</span @if (!hideBadges) {
> @for (provider of permission.grantedProviders; track $index) {
} <span class="badge bg-primary text-dark"
>{{ provider.providerName }}: {{ provider.providerKey }}</span
>
}
}
</label>
</div>
} }
</label> }
</div> </div>
} </ng-template>
} </div>
</div> }
</div> </div>
</div> </div>
</fieldset> </fieldset>

22
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts

@ -34,6 +34,8 @@ import { PermissionManagement } from '../models';
import { NgStyle } from '@angular/common'; import { NgStyle } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Tabs, TabList, Tab, TabPanel, TabContent } from '@angular/aria/tabs';
type PermissionWithStyle = PermissionGrantInfoDto & { type PermissionWithStyle = PermissionGrantInfoDto & {
style: string; style: string;
}; };
@ -56,7 +58,7 @@ type PermissionWithGroupName = PermissionGrantInfoDto & {
max-height: calc(100vh - 23.1rem); max-height: calc(100vh - 23.1rem);
} }
.lpx-scroll-pills-container ul { .lpx-scroll-pills-container .nav-pills {
display: block; display: block;
overflow-y: auto; overflow-y: auto;
} }
@ -66,7 +68,7 @@ type PermissionWithGroupName = PermissionGrantInfoDto & {
.scroll-in-modal { .scroll-in-modal {
max-height: calc(100vh - 15rem); max-height: calc(100vh - 15rem);
} }
.lpx-scroll-pills-container ul { .lpx-scroll-pills-container .nav-pills {
max-height: 500px; max-height: 500px;
} }
} }
@ -81,12 +83,12 @@ type PermissionWithGroupName = PermissionGrantInfoDto & {
padding-bottom: 0 !important; padding-bottom: 0 !important;
} }
.lpx-scroll-pills-container ul li { .lpx-scroll-pills-container .nav-item {
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 10px; border-radius: 10px;
} }
.lpx-scroll-pills-container ul li a.active { .lpx-scroll-pills-container .nav-item .nav-link.active {
color: #fff !important; color: #fff !important;
border-color: #6c5dd3 !important; border-color: #6c5dd3 !important;
background-color: #6c5dd3 !important; background-color: #6c5dd3 !important;
@ -100,6 +102,11 @@ type PermissionWithGroupName = PermissionGrantInfoDto & {
LocalizationPipe, LocalizationPipe,
ButtonComponent, ButtonComponent,
ModalCloseDirective, ModalCloseDirective,
Tabs,
TabList,
Tab,
TabPanel,
TabContent,
], ],
}) })
export class PermissionManagementComponent export class PermissionManagementComponent
@ -418,6 +425,13 @@ export class PermissionManagementComponent
this.onChangeGroup(this.selectedGroup); this.onChangeGroup(this.selectedGroup);
} }
onTabChange(groupName: string) {
const group = this.permissionGroups().find(g => g.name === groupName);
if (group) {
this.onChangeGroup(group);
}
}
onChangeGroup(group: PermissionGroupDto) { onChangeGroup(group: PermissionGroupDto) {
this.setDisabled(group.permissions); this.setDisabled(group.permissions);
this.setSelectedGroup(group); this.setSelectedGroup(group);

2
npm/ng-packs/packages/setting-management/package.json

@ -12,7 +12,7 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/aria": "21.0.0" "@angular/aria": "~21.0.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

Loading…
Cancel
Save