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:
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { BadgeDirective, BadgeTypes } from "./badge.directive";
|
||||
export { BadgeDirective, BadgeType } from "./badge.directive";
|
||||
export * from "./badge.module";
|
||||
|
||||
@@ -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;
|
||||
|
||||
56
libs/components/src/form-field/bit-validators.stories.ts
Normal file
56
libs/components/src/form-field/bit-validators.stories.ts
Normal 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,
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
1
libs/components/src/form-field/bit-validators/index.ts
Normal file
1
libs/components/src/form-field/bit-validators/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { forbiddenCharacters } from "./forbidden-characters.validator";
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./multi-select.module";
|
||||
export * from "./models/select-item-view";
|
||||
|
||||
Reference in New Issue
Block a user