1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00

[EC-73] edit collection modal (#3638)

* [EC-16] Cleanup RxJS linting problems

* [EC-16] Update Group tab to use table component and show collections.

* [EC-16] Extract interface from GroupResponse and use it in the view

* [EC-16] Remove heading underline

* [EC-16] Cleanup i18n

* [EC-16] More i18n cleanup

* [EC-16] Fix bulk group request type name

* [EC-16] Rename group details type

* [EC-73] feat: add inital version of modal using dialog service

* [EC-73] feat: create story for dialog

* [EC-73] feat: setup story with support for injected data

* [EC-73] feat: add inital version of subtitle

* [EC-73] feat: add tabs

* [EC-73] feat: initial version of collection info form

* [EC-73] feat: start of working form

* [EC-73] feat: add custom form validator

* [EC-73] fix: dialog directive names after rebase

* [EC-73] feat: use custom validator

* [EC-73] fix: story

* [EC-73] feat: allow parent picking

* [EC-73] feat: remove tabs to allow for merging

* [EC-73] feat: extend story with new and edit dialogs

* [EC-73] feat: change title depending on if editing or not

* [EC-73] fix: parent not connected to form

* [EC-73] feat: add organizationId to dialog data

* [EC-73] feat: only allow nesting within collections with access

* [EC-73] feat: handle loading with spinner

* [EC-73] feat: update collections on submit

* [EC-73] feat: reload on save

* [EC-73] feat: update story to work with latest changes

* [EC-73] feat: always fetch collections from server

* [EC-73] fix: do not submit if form invalid

* [EC-73] feat: create new collections using new ui

* [EC-73] fix: external id not being saved

* [EC-73] chore: move calls to separete collection admin service

* [EC-73] feat: use new admin views

* [EC-73] feat: implement deletion

* [EC-73] feat: add support for collection details in service

* [EC-73] fix: story

* [EC-73] fix: cancel button

* [EC-73] feat: re-add tabs

* [EC-73] fix: jslib service collection deps

* [EC-73] chore: rename component to collection-dialog

* [EC-73] chore: clean up collection api service which was replaced

* [EC-73] chore: restore collection.service

* [EC-73] chore: restore dialog component changes

* [EC-73] fix: move subscription to ngOnInit

* [EC-73] feat: disable padding when using tabbed content

* [EC-73] fix: new lint rules after merge

* Add Access Selector Component and Stories

* Cherry pick FormSelectionList

* Fix some problems caused from cherry-pick

* Fix some Web module problems caused from cherry-pick

* Move AccessSelector out of the root components directory.

Move UserType pipe to AccessSelectorModule

* Fix broken member access selector story

* Add organization feature module

* Undo changes to messages.json

* Fix messages.json

* Remove redundant CommonModule

* [EC-86] Clear collectionMap before populating it with new collections

* [EC-86] Update initialization/loading logic to make better use of the Observable pattern

* [EC-86] Make table cells use a pointer cursor

* [EC-86] Use bitIconButton for row menu triggers

* [EC-86] Refactor GroupDetailsRow interface to wrap GroupDetailsResponse.

 Remove response model interfaces.

 Cleanup GroupsComponent.

* [EC-86] Add bit-badge-list component and tweak BadgeModule to support both the component and directive.

Update mockI18nService to support templated strings.

* [EC-86] Cleanup badge color and bitIconButton classes

* [EC-86] Cleanup more styles

* [EC-86] Add GroupApiService

Add a new GroupApiService to replace Group Api calls in the ApiService.

* [EC-599] Fix avatar/icon sizing

* [EC-599] Remove padding in  permission column

* [EC-599] Make FormSelectionList operations immutable

* [EC-599] Integrate the multi-select component

* [EC-599] Handle readonly/access all edge cases

* [EC-599] Add initial unit tests

Also cleans up public interface for the AccessSelectorComponent. Fixes a bug found during unit test creation.

* [EC-599] Include item name in control labels

* [EC-599] Cleanup member email display

* [EC-86] Revisions for badge-list implementation.

- Remove `| null` for maxItems according to ADR-0014
- Remove custom setter for items
- Use ngOnChanges to update filteredItems
- Fix sr-only tailwind class and show screen reader comma after last item if truncated.

* [EC-86] Refactor badge-list module/component

- Move the badge list component to its own module.
- Extract badge list stories from badge stories.
- Cleanup bade stories and module after refactor.

* [EC-86] Refactor/rename GroupApiService

- Re-name GroupApiService to GroupService
 as there is no need for a separate Api service (no sync or local data for admin services)
- Add GroupView for use in the GroupService instead of raw API models
- Update views to use GroupView instead of raw GroupResponse models

* [EC-86] Refactor group API request models

- Move organizationGroupBulkRequest to group requests folder
- Fix relative imports in GroupService

* [EC-86] Fix linting errors

* Fix tab item text color

Tab item text color broke after a merge from master and needs a fix to account for bootstrap styles in Web.

* [EC-599] Review suggestions

- Change PermissionMode to Enum
- Rename permControl to permissionControl to be more clear
- Rename FormSelectionList file to kebab case.
- Move permission row boolean logic to named function for readability

* [EC-599] Cleanup AccessSelectorComponent tests

- Clarify test states
- Add tests for column rendering
- Add tests for permission mode
- Add id to column headers for testing
- Fix small permissionControl bug found during testing

* [EC-599] Add FormSelectionList unit tests

* [EC-73] chore: re-add collections page

* [EC-86] Rename new files using kebab-case

* [EC-73] chore: move component to shared org module

* Fix MultiSelect component styles and CSP error (#3841)

* Update Web styles and CSP to support MultiSelect component

- Include the MultiSelect module in the CL barrel file of exports
- Import the MultiSelect scss into the Web styles.scss
- Add the necessary sha256 hash to webpack CSP policy to support ngSelect inline styles

* Undo removal of 127.0.0.1 from webpack CSP

(cherry picked from commit 3ed1221f7f)

* [EC-73] feat: add empty access selector

* [EC-73] feat: add groups to access selector

* [EC-73] chore: improve storybook support

* [EC-73] feat: tweak item assignment

* [EC-73] feat: add support for showing users

* [EC-73] feat: use async actions

* [EC-73] chore: clean up casting

* [EC-73] fix: permissions not loading correctly in access selector

* [EC-73] feat: implement saving group permissions

* [EC-73] feat: rename to collection access selection view

* [EC-73] feat: save users as well

* [EC-73] fix: access selector usage

* [EC-73] feat: new collection creation

* [EC-73] feat: fetch users from collection details

* [EC-73] chore: clean up

* [EC-73] fix: circular dependency issues

* [EC-73] fix: import shared module directly to workaround build issues

* [EC-73] fix: missing dependencies in story

* [EC-73] chore: move story

* [EC-73] fix: manual cherry pick permission bug fix

* [EC-73] feat: hide delete button if no permission

* [EC-73] feat: properly handle orgs without groups

* [EC-73] fix: use correct functions in template

* [EC-73] feat: properly handle non-existing parent

* [EC-73] chore: use double ngIf instead of else template

* [EC-73] fix: add type to dialog ref

* [EC-73] fix: restrict field modifiers

* [EC-73] fix: use result enum directly

* [EC-73] fix: simplify mapping logic

* [EC-73]

* [EC-73] feat: add story for free orgs without groups

* [EC-73] fix: parametrized i18n

* [EC-73] feat: create new shared org module

* [EC-73] feat: move collection dialog to shared

* [EC-73] feat: move access selector to shared

* [EC-73] feat: create core organization module

* [EC-73] feat: move collection admin service to web

* [EC-73] feat: move collection admin views to web

* [EC-73] fix: missing i18n

* [EC-73] fix: refactor for type safety

* [EC-73] fix: storybook not compiling again

* [EC-73] feat: use helper function to open dialog

* [EC-73] chore: remove comment

* [EC-73] fix: revert permission fix

* [EC-73] fix: only show delete if in edit mode

* [EC-73] chore: remove ngIf else in template

* [EC-73] fix: add missing appA11yTitle

* [EC-73] chore: rename remove to delete

* [EC-73] chore: refactor ngOnInit

* [EC-73] fix: dialog position strategy

* [EC-73] fix: revert spinner to old way of doing it

Signed-off-by: Jacob Fink <jfink@bitwarden.com>
Co-authored-by: Shane Melton <smelton@bitwarden.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com>
This commit is contained in:
Andreas Coroiu
2022-11-22 14:33:47 +01:00
committed by GitHub
parent 21a9f84956
commit 39655ebe29
57 changed files with 1225 additions and 420 deletions

View File

@@ -3,10 +3,9 @@ import { Subject, takeUntil } from "rxjs";
import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
import { BitActionDirective } from "./bit-action.directive";
import { BitSubmitDirective } from "./bit-submit.directive";
import { BitActionDirective } from ".";
/**
* This directive has two purposes:
*

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnChanges } from "@angular/core";
import { BadgeTypes } from "../badge";
import { BadgeType } from "../badge";
@Component({
selector: "bit-badge-list",
@@ -12,7 +12,7 @@ export class BadgeListComponent implements OnChanges {
protected filteredItems: string[] = [];
protected isFiltered = false;
@Input() badgeType: BadgeTypes = "primary";
@Input() badgeType: BadgeType = "primary";
@Input() items: string[] = [];
@Input()

View File

@@ -1,8 +1,8 @@
import { Directive, ElementRef, HostBinding, Input } from "@angular/core";
export type BadgeTypes = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
export type BadgeType = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
const styles: Record<BadgeTypes, string[]> = {
const styles: Record<BadgeType, string[]> = {
primary: ["tw-bg-primary-500"],
secondary: ["tw-bg-text-muted"],
success: ["tw-bg-success-500"],
@@ -11,7 +11,7 @@ const styles: Record<BadgeTypes, string[]> = {
info: ["tw-bg-info-500"],
};
const hoverStyles: Record<BadgeTypes, string[]> = {
const hoverStyles: Record<BadgeType, string[]> = {
primary: ["hover:tw-bg-primary-700"],
secondary: ["hover:tw-bg-secondary-700"],
success: ["hover:tw-bg-success-700"],
@@ -47,7 +47,7 @@ export class BadgeDirective {
.concat(this.hasHoverEffects ? hoverStyles[this.badgeType] : []);
}
@Input() badgeType: BadgeTypes = "primary";
@Input() badgeType: BadgeType = "primary";
private hasHoverEffects = false;

View File

@@ -1,7 +1,7 @@
import { CommonModule } from "@angular/common";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { BadgeDirective } from "./badge.directive";
import { BadgeDirective, BadgeType } from "./badge.directive";
export default {
title: "Component Library/Badge",
@@ -15,6 +15,12 @@ export default {
args: {
badgeType: "primary",
},
argTypes: {
badgeType: {
options: ["primary", "secondary", "success", "danger", "warning", "info"] as BadgeType[],
control: { type: "inline-radio" },
},
},
parameters: {
design: {
type: "figma",

View File

@@ -1,2 +1,2 @@
export { BadgeDirective, BadgeTypes } from "./badge.directive";
export { BadgeDirective, BadgeType } from "./badge.directive";
export * from "./badge.module";

View File

@@ -4,7 +4,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { I18nMockService } from "../utils/i18n-mock.service";
import { CalloutComponent } from ".";
import { CalloutComponent } from "./callout.component";
describe("Callout", () => {
let component: CalloutComponent;

View File

@@ -0,0 +1,56 @@
import { FormsModule, ReactiveFormsModule, FormBuilder } from "@angular/forms";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { ButtonModule } from "../button";
import { InputModule } from "../input/input.module";
import { I18nMockService } from "../utils/i18n-mock.service";
import { forbiddenCharacters } from "./bit-validators/forbidden-characters.validator";
import { BitFormFieldComponent } from "./form-field.component";
import { FormFieldModule } from "./form-field.module";
export default {
title: "Component Library/Form/Custom Validators",
component: BitFormFieldComponent,
decorators: [
moduleMetadata({
imports: [FormsModule, ReactiveFormsModule, FormFieldModule, InputModule, ButtonModule],
providers: [
{
provide: I18nService,
useFactory: () => {
return new I18nMockService({
inputForbiddenCharacters: (chars) =>
`The following characters are not allowed: ${chars}`,
});
},
},
],
}),
],
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/f32LSg3jaegICkMu7rPARm/Tailwind-Component-Library-Update?node-id=1881%3A17689",
},
},
} as Meta;
const template = `
<form [formGroup]="formObj">
<bit-form-field>
<bit-label>Name</bit-label>
<input bitInput formControlName="name" />
</bit-form-field>
</form>`;
export const ForbiddenCharacters: Story<BitFormFieldComponent> = (args: BitFormFieldComponent) => ({
props: {
formObj: new FormBuilder().group({
name: ["", forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"])],
}),
},
template,
});

View File

@@ -0,0 +1,45 @@
import { FormControl } from "@angular/forms";
import { forbiddenCharacters } from "./forbidden-characters.validator";
describe("forbiddenCharacters", () => {
it("should return no error when input is null", () => {
const input = createControl(null);
const validate = forbiddenCharacters(["n", "u", "l", "l"]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return no error when no characters are forbidden", () => {
const input = createControl("special characters: \\/@#$%^&*()");
const validate = forbiddenCharacters([]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return no error when input does not contain forbidden characters", () => {
const input = createControl("contains no special characters");
const validate = forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return error when input contains forbidden characters", () => {
const input = createControl("contains / illegal @ characters");
const validate = forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"]);
const errors = validate(input);
expect(errors).not.toBe(null);
});
});
function createControl(input: string) {
return new FormControl(input);
}

View File

@@ -0,0 +1,23 @@
import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from "@angular/forms";
export function forbiddenCharacters(characters: string[]): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!(control instanceof FormControl)) {
throw new Error("forbiddenCharacters only supports validating FormControls");
}
if (control.value === null || control.value === undefined) {
return null;
}
const value = String(control.value);
for (const char of value) {
if (characters.includes(char)) {
return { forbiddenCharacters: { value: control.value, characters } };
}
}
return null;
};
}

View File

@@ -0,0 +1 @@
export { forbiddenCharacters } from "./forbidden-characters.validator";

View File

@@ -30,6 +30,8 @@ export class BitErrorComponent {
return this.i18nService.t("inputMinLength", this.error[1]?.requiredLength);
case "maxlength":
return this.i18nService.t("inputMaxLength", this.error[1]?.requiredLength);
case "forbiddenCharacters":
return this.i18nService.t("inputForbiddenCharacters", this.error[1]?.characters.join(", "));
default:
// Attempt to show a custom error message.
if (this.error[1]?.message) {

View File

@@ -1,3 +1,4 @@
export * from "./form-field.module";
export * from "./form-field.component";
export * from "./form-field-control";
export * as BitValidators from "./bit-validators";

View File

@@ -1 +1,2 @@
export * from "./multi-select.module";
export * from "./models/select-item-view";