Merge pull request #9636 from opf/feature/38723-notification-settings-frontend
Feature/38723 notification settings frontendpull/9687/head
commit
6bf3475348
@ -0,0 +1,3 @@ |
|||||||
|
.op-autocompleter |
||||||
|
display: block |
||||||
|
max-width: 600px |
@ -1,16 +1,117 @@ |
|||||||
<op-notifications-settings-toolbar></op-notifications-settings-toolbar> |
<op-notifications-settings-toolbar></op-notifications-settings-toolbar> |
||||||
<op-notification-settings-table |
<form |
||||||
|
[formGroup]="form" |
||||||
|
(ngSubmit)="saveChanges()" |
||||||
|
class="op-form" |
||||||
|
> |
||||||
|
<h5>{{ text.notifyImmediately.title }}</h5> |
||||||
|
<p>{{ text.notifyImmediately.description }}</p> |
||||||
|
|
||||||
|
<op-checkbox-field [label]="text.mentioned.title"> |
||||||
|
<input |
||||||
|
disabled |
||||||
|
checked |
||||||
|
type="checkbox" |
||||||
|
slot="input" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.mentioned.description }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field |
||||||
|
[label]="text.involved.title" |
||||||
|
[control]="form.get('involved')" |
||||||
|
> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="involved" |
||||||
|
data-qa-global-notification-type="involved" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.mentioned.title }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<h5>{{ text.alsoNotifyFor.title }}</h5> |
||||||
|
<p>{{ text.alsoNotifyFor.description }}</p> |
||||||
|
|
||||||
|
<op-checkbox-field> |
||||||
|
<input |
||||||
|
disabled |
||||||
|
checked |
||||||
|
type="checkbox" |
||||||
|
slot="input" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.watched }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field [control]="form.get('workPackageCreated')"> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageCreated" |
||||||
|
data-qa-global-notification-type="work_package_created" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.work_package_created }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field [control]="form.get('workPackageProcessed')"> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageProcessed" |
||||||
|
data-qa-global-notification-type="work_package_processed" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.work_package_processed }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field [control]="form.get('workPackageScheduled')"> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageScheduled" |
||||||
|
data-qa-global-notification-type="work_package_scheduled" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.work_package_scheduled }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field [control]="form.get('workPackagePrioritized')"> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackagePrioritized" |
||||||
|
data-qa-global-notification-type="work_package_prioritized" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.work_package_prioritized }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<op-checkbox-field [control]="form.get('workPackageCommented')"> |
||||||
|
<input |
||||||
|
slot="input" |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageCommented" |
||||||
|
data-qa-global-notification-type="work_package_commented" |
||||||
|
/> |
||||||
|
<p slot="description">{{ text.work_package_commented }}</p> |
||||||
|
</op-checkbox-field> |
||||||
|
|
||||||
|
<hr /> |
||||||
|
|
||||||
|
<h5>Project-specific notification settings</h5> |
||||||
|
<p>These project-specific settings override default settings above</p> |
||||||
|
|
||||||
|
<op-notification-settings-table |
||||||
*ngIf="userId" |
*ngIf="userId" |
||||||
[userId]="userId" |
[userId]="userId" |
||||||
></op-notification-settings-table> |
[settings]="form.controls.projectSettings" |
||||||
|
formArrayName="projectSettings" |
||||||
|
class="op-notification-settings-page--table" |
||||||
|
></op-notification-settings-table> |
||||||
|
|
||||||
<div class="generic-table--action-buttons"> |
<div class="generic-table--action-buttons"> |
||||||
<button |
<button |
||||||
class="button -highlight" |
class="button -highlight" |
||||||
[textContent]="text.save" |
[textContent]="text.save" |
||||||
(click)="saveChanges()" |
type="submit" |
||||||
> |
></button> |
||||||
</button> |
</div> |
||||||
</div> |
</form> |
||||||
|
|
||||||
|
|
||||||
|
@ -1,116 +0,0 @@ |
|||||||
<td |
|
||||||
*ngIf="first" |
|
||||||
[attr.rowspan]="count" |
|
||||||
> |
|
||||||
<span |
|
||||||
*ngIf="setting._links.project.href; else defaultTitle" |
|
||||||
[textContent]="setting._links.project.title" |
|
||||||
></span> |
|
||||||
<ng-template #defaultTitle> |
|
||||||
<em [textContent]="text.default_all_projects"></em> |
|
||||||
</ng-template> |
|
||||||
</td> |
|
||||||
<td [textContent]="text.channel(setting.channel)"> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.involved || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ involved: $event.target.checked })" |
|
||||||
data-qa-notification-type="involved" |
|
||||||
[attr.aria-label]="text.involved_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.mentioned || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ mentioned: $event.target.checked })" |
|
||||||
data-qa-notification-type="mentioned" |
|
||||||
[attr.aria-label]="text.mentioned_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.watched || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ watched: $event.target.checked })" |
|
||||||
data-qa-notification-type="watched" |
|
||||||
[attr.aria-label]="text.watched_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.workPackageCreated || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ workPackageCreated: $event.target.checked })" |
|
||||||
data-qa-notification-type="work_package_created" |
|
||||||
[attr.aria-label]="text.work_package_created_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.workPackageCommented || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ workPackageCommented: $event.target.checked })" |
|
||||||
data-qa-notification-type="work_package_commented" |
|
||||||
[attr.aria-label]="text.work_package_commented_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.workPackageProcessed || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ workPackageProcessed: $event.target.checked })" |
|
||||||
data-qa-notification-type="work_package_processed" |
|
||||||
[attr.aria-label]="text.work_package_processed_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.workPackagePrioritized || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ workPackagePrioritized: $event.target.checked })" |
|
||||||
data-qa-notification-type="work_package_prioritized" |
|
||||||
[attr.aria-label]="text.work_package_prioritized_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.workPackageScheduled || setting.all" |
|
||||||
[disabled]="setting.all" |
|
||||||
(change)="update({ workPackageScheduled: $event.target.checked })" |
|
||||||
data-qa-notification-type="work_package_scheduled" |
|
||||||
[attr.aria-label]="text.work_package_scheduled_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td> |
|
||||||
<input |
|
||||||
type="checkbox" |
|
||||||
[checked]="setting.all" |
|
||||||
(change)="update({ all: $event.target.checked })" |
|
||||||
data-qa-notification-type="all" |
|
||||||
[attr.aria-label]="text.any_event_header" |
|
||||||
/> |
|
||||||
</td> |
|
||||||
<td |
|
||||||
*ngIf="first" |
|
||||||
[attr.rowspan]="count" |
|
||||||
class="buttons" |
|
||||||
> |
|
||||||
<button |
|
||||||
*ngIf="!global" |
|
||||||
class="op-link" |
|
||||||
(click)="remove()" |
|
||||||
> |
|
||||||
<op-icon icon-classes="icon-remove icon-no-color"></op-icon> |
|
||||||
</button> |
|
||||||
</td> |
|
@ -1,80 +0,0 @@ |
|||||||
import { |
|
||||||
ChangeDetectionStrategy, |
|
||||||
Component, |
|
||||||
Input, |
|
||||||
OnInit, |
|
||||||
} from '@angular/core'; |
|
||||||
import { I18nService } from 'core-app/core/i18n/i18n.service'; |
|
||||||
import { arrayUpdate } from '@datorama/akita'; |
|
||||||
import { NotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; |
|
||||||
import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; |
|
||||||
|
|
||||||
@Component({ |
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
|
||||||
selector: '[op-notification-setting-row]', |
|
||||||
templateUrl: './notification-setting-row.component.html', |
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush, |
|
||||||
}) |
|
||||||
export class NotificationSettingRowComponent implements OnInit { |
|
||||||
@Input() first = false; |
|
||||||
|
|
||||||
@Input() count:number; |
|
||||||
|
|
||||||
@Input() setting:NotificationSetting; |
|
||||||
|
|
||||||
/** Whether this setting is global */ |
|
||||||
global = false; |
|
||||||
|
|
||||||
text = { |
|
||||||
title: this.I18n.t('js.notifications.settings.title'), |
|
||||||
save: this.I18n.t('js.button_save'), |
|
||||||
email: this.I18n.t('js.notifications.email'), |
|
||||||
inApp: this.I18n.t('js.notifications.in_app'), |
|
||||||
remove_all: this.I18n.t('js.notifications.settings.remove_all'), |
|
||||||
involved_header: this.I18n.t('js.notifications.settings.involved'), |
|
||||||
mentioned_header: this.I18n.t('js.notifications.settings.mentioned'), |
|
||||||
watched_header: this.I18n.t('js.notifications.settings.watched'), |
|
||||||
work_package_commented_header: this.I18n.t('js.notifications.settings.work_package_commented'), |
|
||||||
work_package_created_header: this.I18n.t('js.notifications.settings.work_package_created'), |
|
||||||
work_package_processed_header: this.I18n.t('js.notifications.settings.work_package_processed'), |
|
||||||
work_package_prioritized_header: this.I18n.t('js.notifications.settings.work_package_prioritized'), |
|
||||||
work_package_scheduled_header: this.I18n.t('js.notifications.settings.work_package_scheduled'), |
|
||||||
any_event_header: this.I18n.t('js.notifications.settings.all'), |
|
||||||
default_all_projects: this.I18n.t('js.notifications.settings.default_all_projects'), |
|
||||||
add_setting: this.I18n.t('js.notifications.settings.add'), |
|
||||||
channel: (channel:string):string => this.I18n.t(`js.notifications.channels.${channel}`), |
|
||||||
}; |
|
||||||
|
|
||||||
constructor( |
|
||||||
private I18n:I18nService, |
|
||||||
private storeService:UserPreferencesService, |
|
||||||
) { |
|
||||||
} |
|
||||||
|
|
||||||
ngOnInit() { |
|
||||||
this.global = this.setting._links.project.href === null; |
|
||||||
} |
|
||||||
|
|
||||||
remove():void { |
|
||||||
this.storeService.store.update( |
|
||||||
({ notifications }) => ({ |
|
||||||
notifications: notifications.filter((notification) => notification._links.project.href !== this.setting._links.project.href), |
|
||||||
}), |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
update(delta:Partial<NotificationSetting>) { |
|
||||||
this.storeService.store.update( |
|
||||||
({ notifications }) => ({ |
|
||||||
notifications: arrayUpdate( |
|
||||||
notifications, this.matcherFn.bind(this), delta, |
|
||||||
), |
|
||||||
}), |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
private matcherFn(notification:NotificationSetting) { |
|
||||||
return notification._links.project.href === this.setting._links.project.href |
|
||||||
&& notification.channel === this.setting.channel; |
|
||||||
} |
|
||||||
} |
|
@ -1,125 +1,142 @@ |
|||||||
<div class="generic-table--container form--section"> |
<div class="op-scrollable-table"> |
||||||
<div class="generic-table--results-container"> |
<table |
||||||
<table class="generic-table"> |
class="op-table" |
||||||
<colgroup> |
*ngIf="settings.length > 0" |
||||||
<col> |
> |
||||||
<col opHighlightCol> |
<thead> |
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
<col opHighlightCol> |
|
||||||
</colgroup> |
|
||||||
<thead> |
|
||||||
<tr> |
<tr> |
||||||
<th> |
<th class="op-table--cell op-table--cell_heading"> |
||||||
<div class="generic-table--empty-header"></div> |
{{ text.notify_me }} |
||||||
</th> |
</th> |
||||||
<th> |
<ng-container *ngFor="let item of settings.controls"> |
||||||
<div class="generic-table--sort-header-outer"> |
<th class="op-table--cell op-table--cell_heading"> |
||||||
<div class="generic-table--sort-header"> |
<a |
||||||
<span [textContent]="text.channel_header"></span> |
class="op-link" |
||||||
</div> |
[href]="projectLink(item.controls.project.value.href)" |
||||||
</div> |
>{{ item.controls.project.value.title }}</a> |
||||||
</th> |
</th> |
||||||
<th> |
</ng-container> |
||||||
<div class="generic-table--sort-header-outer"> |
</tr> |
||||||
<div class="generic-table--sort-header"> |
</thead> |
||||||
<span [textContent]="text.involved_header"></span> |
<tbody> |
||||||
</div> |
<tr> |
||||||
</div> |
<th class="op-table--cell op-table--cell_soft-heading"> |
||||||
</th> |
<h5>{{ text.mentioned_header.title }}</h5> |
||||||
<th> |
<p>{{ text.mentioned_header.description }}</p> |
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.mentioned_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.watched_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.work_package_created_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.work_package_commented_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.work_package_processed_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.work_package_prioritized_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.work_package_scheduled_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
|
||||||
<th> |
|
||||||
<div class="generic-table--sort-header-outer"> |
|
||||||
<div class="generic-table--sort-header"> |
|
||||||
<span [textContent]="text.any_event_header"></span> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</th> |
</th> |
||||||
<th> |
<ng-container *ngFor="let item of settings.controls"> |
||||||
<div class="generic-table--empty-header"></div> |
<td class="op-table--cell"><input type="checkbox" disabled checked /></td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th class="op-table--cell op-table--cell_soft-heading"> |
||||||
|
<h5>{{ text.involved_header.title }}</h5> |
||||||
|
<p>{{ text.involved_header.description }}</p> |
||||||
</th> |
</th> |
||||||
|
<ng-container *ngFor="let item of settings.controls"> |
||||||
|
<td class="op-table--cell" [formGroup]="item"> |
||||||
|
<input |
||||||
|
type="checkbox" |
||||||
|
formControlName="involved" |
||||||
|
data-qa-project-notification-type="involved" |
||||||
|
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
|
/> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th class="op-table--cell op-table--cell_soft-heading">{{ text.watched_header }}</th> |
||||||
|
<ng-container *ngFor="let item of settings.controls"> |
||||||
|
<td class="op-table--cell"><input type="checkbox" disabled checked /></td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th class="op-table--cell op-table--cell_soft-heading">{{ text.work_package_created_header }}</th> |
||||||
|
<ng-container *ngFor="let item of settings.controls"> |
||||||
|
<td class="op-table--cell" [formGroup]="item"> |
||||||
|
<input |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageCreated" |
||||||
|
data-qa-project-notification-type="work_package_created" |
||||||
|
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
|
/> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th class="op-table--cell op-table--cell_soft-heading">{{ text.work_package_processed_header }}</th> |
||||||
|
<ng-container *ngFor="let item of settings.controls"> |
||||||
|
<td class="op-table--cell" [formGroup]="item"> |
||||||
|
<input |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageProcessed" |
||||||
|
data-qa-project-notification-type="work_package_processed" |
||||||
|
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
|
/> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th class="op-table--cell op-table--cell_soft-heading">{{ text.work_package_scheduled_header }}</th> |
||||||
|
<ng-container *ngFor="let item of settings.controls"> |
||||||
|
<td class="op-table--cell" [formGroup]="item"> |
||||||
|
<input |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageScheduled" |
||||||
|
data-qa-project-notification-type="work_package_scheduled" |
||||||
|
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
|
/> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
</tr> |
</tr> |
||||||
</thead> |
<tr> |
||||||
<tbody> |
<th class="op-table--cell op-table--cell_soft-heading">{{ text.work_package_prioritized_header }}</th> |
||||||
<ng-container *ngFor="let item of (groupedNotificationSettings$ | async) | keyvalue: projectOrder"> |
<ng-container *ngFor="let item of settings.controls"> |
||||||
<ng-container *ngFor="let setting of item.value; let first = first; let last = last"> |
<td class="op-table--cell" [formGroup]="item"> |
||||||
<tr |
<input |
||||||
class="-no-highlighting" |
type="checkbox" |
||||||
op-notification-setting-row |
formControlName="workPackagePrioritized" |
||||||
[attr.data-qa-notification-project]="item.key" |
data-qa-project-notification-type="work_package_prioritized" |
||||||
[attr.data-qa-notification-channel]="setting.channel" |
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
[count]="item.value.length" |
/> |
||||||
[first]="first" |
</td> |
||||||
[setting]="setting" |
</ng-container> |
||||||
> |
</tr> |
||||||
</tr> |
<tr> |
||||||
<tr *ngIf="last" |
<th class="op-table--cell op-table--cell_soft-heading">{{ text.work_package_commented_header }}</th> |
||||||
class="op-notifications-settings-table--spacer"> |
<ng-container *ngFor="let item of settings.controls"> |
||||||
<td colspan="7"></td> |
<td class="op-table--cell" [formGroup]="item"> |
||||||
</tr> |
<input |
||||||
|
type="checkbox" |
||||||
|
formControlName="workPackageCommented" |
||||||
|
data-qa-project-notification-type="work_package_commented" |
||||||
|
[attr.data-qa-project]="item.controls.project.value.title" |
||||||
|
/> |
||||||
|
</td> |
||||||
</ng-container> |
</ng-container> |
||||||
</ng-container> |
</tr> |
||||||
</tbody> |
<tr> |
||||||
</table> |
<th class="op-table--cell op-table--cell_soft-heading"></th> |
||||||
<op-notification-setting-inline-create |
<ng-container *ngFor="let item of settings.controls; let index = index"> |
||||||
*ngIf="userId" |
<td class="op-table--cell"> |
||||||
[userId]="userId" |
<button |
||||||
(selected)="addRow($event)" |
type="button" |
||||||
data-qa-selector="notification-setting-inline-create" |
class="op-link" |
||||||
></op-notification-setting-inline-create> |
(click)="removeProjectSettings(index)" |
||||||
</div> |
> |
||||||
</div> |
Remove Project Settings |
||||||
|
</button> |
||||||
|
</td> |
||||||
|
</ng-container> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<op-notification-setting-inline-create |
||||||
|
*ngIf="userId" |
||||||
|
[userId]="userId" |
||||||
|
[settings]="settings" |
||||||
|
(selected)="addProjectSettings($event)" |
||||||
|
data-qa-selector="notification-setting-inline-create" |
||||||
|
></op-notification-setting-inline-create> |
@ -1,6 +1,2 @@ |
|||||||
.op-notifications-settings-table |
.op-table |
||||||
&--spacer |
margin-bottom: 1rem |
||||||
|
|
||||||
// The default table styles are a mess |
|
||||||
td |
|
||||||
padding: 0 !important |
|
@ -0,0 +1,45 @@ |
|||||||
|
<ng-container *ngIf="!hidden"> |
||||||
|
<label class="op-form-field--label-wrap op-checkbox-field--label-wrap"> |
||||||
|
<div |
||||||
|
class="op-form-field--input op-checkbox-field--input" |
||||||
|
[attr.aria-describedby]="describedByID" |
||||||
|
> |
||||||
|
<ng-content select="[slot=input]"></ng-content> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div |
||||||
|
*ngIf="label" |
||||||
|
class="op-form-field--label op-checkbox-field--label" |
||||||
|
> |
||||||
|
<span |
||||||
|
*ngIf="showErrorMessage" |
||||||
|
class="Hidden for sighted" |
||||||
|
>Invalid</span> |
||||||
|
{{ label }} |
||||||
|
<span *ngIf="required" class="op-form-field--label-indicator">*</span> |
||||||
|
<attribute-help-text |
||||||
|
[attribute]="helpTextAttribute" |
||||||
|
[attributeScope]="helpTextAttributeScope" |
||||||
|
></attribute-help-text> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div |
||||||
|
class="op-form-field--description op-checkbox-field--description" |
||||||
|
[id]="descriptionID" |
||||||
|
> |
||||||
|
<ng-content select="[slot=description]"></ng-content> |
||||||
|
</div> |
||||||
|
</label> |
||||||
|
|
||||||
|
<div class="op-form-field--help-text"> |
||||||
|
<ng-content select="[slot=help-text]"></ng-content> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div |
||||||
|
class="op-form-field--errors" |
||||||
|
*ngIf="showErrorMessage" |
||||||
|
[id]="errorsID" |
||||||
|
> |
||||||
|
<ng-content select="[slot=errors]"></ng-content> |
||||||
|
</div> |
||||||
|
</ng-container> |
@ -0,0 +1,80 @@ |
|||||||
|
import { |
||||||
|
Component, |
||||||
|
ContentChild, |
||||||
|
HostBinding, |
||||||
|
Input, |
||||||
|
Optional, |
||||||
|
} from '@angular/core'; |
||||||
|
import { |
||||||
|
AbstractControl, |
||||||
|
FormGroupDirective, |
||||||
|
NgControl, |
||||||
|
} from '@angular/forms'; |
||||||
|
|
||||||
|
@Component({ |
||||||
|
selector: 'op-checkbox-field', |
||||||
|
templateUrl: './checkbox-field.component.html', |
||||||
|
}) |
||||||
|
export class OpCheckboxFieldComponent { |
||||||
|
@HostBinding('class.op-form-field') className = true; |
||||||
|
|
||||||
|
@HostBinding('class.op-checkbox-field') classNameCheckbox = true; |
||||||
|
|
||||||
|
@HostBinding('class.op-form-field_invalid') get errorClassName():boolean { |
||||||
|
return this.showErrorMessage; |
||||||
|
} |
||||||
|
|
||||||
|
@Input() label = ''; |
||||||
|
|
||||||
|
@Input() hidden = false; |
||||||
|
|
||||||
|
@Input() required = false; |
||||||
|
|
||||||
|
@Input() showValidationErrorOn:'change' | 'blur' | 'submit' | 'never' = 'submit'; |
||||||
|
|
||||||
|
@Input() control?:AbstractControl; |
||||||
|
|
||||||
|
@Input() helpTextAttribute?:string; |
||||||
|
|
||||||
|
@Input() helpTextAttributeScope?:string; |
||||||
|
|
||||||
|
@ContentChild(NgControl) ngControl:NgControl; |
||||||
|
|
||||||
|
internalID = `op-checkbox-field-${+new Date()}`; |
||||||
|
|
||||||
|
get errorsID():string { |
||||||
|
return `${this.internalID}-errors`; |
||||||
|
} |
||||||
|
|
||||||
|
get descriptionID():string { |
||||||
|
return `${this.internalID}-description`; |
||||||
|
} |
||||||
|
|
||||||
|
get describedByID():string { |
||||||
|
return this.showErrorMessage ? this.errorsID : this.descriptionID; |
||||||
|
} |
||||||
|
|
||||||
|
get formControl():AbstractControl|undefined|null { |
||||||
|
return this.ngControl?.control || this.control; |
||||||
|
} |
||||||
|
|
||||||
|
get showErrorMessage():boolean { |
||||||
|
if (!this.formControl) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (this.showValidationErrorOn === 'submit') { |
||||||
|
return this.formControl.invalid && this.formGroupDirective?.submitted; |
||||||
|
} if (this.showValidationErrorOn === 'blur') { |
||||||
|
return this.formControl.invalid && this.formControl.touched; |
||||||
|
} if (this.showValidationErrorOn === 'change') { |
||||||
|
return this.formControl.invalid && this.formControl.dirty; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
constructor( |
||||||
|
@Optional() private formGroupDirective:FormGroupDirective, |
||||||
|
) {} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
.op-checkbox-field |
||||||
|
&--label-wrap |
||||||
|
display: grid |
||||||
|
grid-template-columns: auto 1fr |
||||||
|
grid-template-rows: auto auto |
||||||
|
|
||||||
|
&--input |
||||||
|
grid-column-start: 1 |
||||||
|
grid-column-end: 2 |
||||||
|
grid-row-start: 1 |
||||||
|
grid-row-end: 3 |
||||||
|
align-self: center |
||||||
|
padding-right: 1rem |
||||||
|
margin-bottom: 0 |
||||||
|
|
||||||
|
&--label |
||||||
|
grid-column-start: 2 |
||||||
|
grid-column-end: 2 |
||||||
|
grid-row-start: 1 |
||||||
|
grid-row-end: 2 |
||||||
|
margin-bottom: 0 |
||||||
|
|
||||||
|
&--description |
||||||
|
grid-column-start: 2 |
||||||
|
grid-column-end: 2 |
||||||
|
grid-row-start: 2 |
||||||
|
grid-row-end: 3 |
||||||
|
margin-bottom: 0 |
||||||
|
|
||||||
|
&--label, |
||||||
|
&--description |
||||||
|
white-space: pre-line |
||||||
|
word-break: break-word |
||||||
|
word-wrap: break-word |
@ -1,4 +1,5 @@ |
|||||||
@import './form-field/form-field' |
@import './form-field/form-field' |
||||||
|
@import './checkbox-field/checkbox-field' |
||||||
@import './form' |
@import './form' |
||||||
@import './fieldset' |
@import './fieldset' |
||||||
@import './highlighted-input' |
@import './highlighted-input' |
||||||
|
@ -0,0 +1,6 @@ |
|||||||
|
.op-scrollable-table |
||||||
|
max-width: 100% |
||||||
|
overflow-x: scroll |
||||||
|
|
||||||
|
.op-table |
||||||
|
width: auto |
@ -0,0 +1,18 @@ |
|||||||
|
.op-table |
||||||
|
border-collapse: collapse |
||||||
|
width: 100% |
||||||
|
|
||||||
|
&--cell |
||||||
|
padding: 12px 16px |
||||||
|
border: 1px solid #cccccc |
||||||
|
text-align: center |
||||||
|
|
||||||
|
&_heading |
||||||
|
background-color: #f3f3f3 |
||||||
|
font-weight: bold |
||||||
|
text-align: left |
||||||
|
|
||||||
|
&_soft-heading |
||||||
|
background-color: transparent |
||||||
|
text-align: left |
||||||
|
font-weight: normal |
Loading…
Reference in new issue