Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CL-27] [EC-455] Ng-Select Integration #3383

Merged
merged 40 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
137f796
[CL-27] Select -> Library initial commit
vincentsalucci Aug 11, 2022
b71dc48
[EC-455] NG-Select Integration
vincentsalucci Aug 25, 2022
16a2227
Prettier
vincentsalucci Aug 25, 2022
bc1d769
Merge branch 'master' into m/ng-select
vincentsalucci Aug 25, 2022
f0b1ac6
Merge branch 'master' into m/ng-select
vincentsalucci Aug 29, 2022
9c1848d
[EC-455] [CL-27] Add option for removing items on close // Added load…
vincentsalucci Aug 30, 2022
25408d2
Merge branch 'master' into m/ng-select
vincentsalucci Aug 30, 2022
c6d2f9a
[EC-455] [CL-27] Removed enter override // Fixed backspace removal //…
vincentsalucci Sep 1, 2022
0710a3c
[EC-455] [CL-27] Added copy from text to theme
vincentsalucci Sep 1, 2022
e7ff7e4
Merge branch 'master' into m/ng-select
vincentsalucci Sep 1, 2022
f3f0fa6
[EC-455] [CL-27] Changed SimpleItemView to type - removed creation lo…
vincentsalucci Sep 1, 2022
1834245
[EC-455] [CL-27] Updated custom theme to include CSS variable colors
vincentsalucci Sep 6, 2022
889c651
Merge branch 'master' into m/ng-select
vincentsalucci Sep 13, 2022
b902fc0
[CL-27] [EC-455] Initial pass at form field control // initial templa…
vincentsalucci Sep 20, 2022
818a3fe
Merge branch 'master' into m/ng-select
vincentsalucci Sep 21, 2022
53e24d6
[EC-455] working baseItem input
vincentsalucci Sep 21, 2022
1396b8d
[EC-455] working value accessor
vincentsalucci Sep 21, 2022
4d585ab
Merge branch 'master' into m/ng-select
vincentsalucci Sep 29, 2022
1eb30f1
[EC-455] Completed FormFieldControl implemntation // fixed badge disa…
vincentsalucci Oct 3, 2022
289ad5d
Merge branch 'master' into m/ng-select
vincentsalucci Oct 3, 2022
f2d1918
[EC-455] Prettier
vincentsalucci Oct 3, 2022
a8d7bbc
Merge branch 'master' into m/ng-select
vincentsalucci Oct 4, 2022
81dcc1b
[EC-455] Removed obsolete variables
vincentsalucci Oct 4, 2022
394e3b5
[EC-455] Private value accessor functions
vincentsalucci Oct 4, 2022
c02ad12
[EC-455] Cleaned up default variables
vincentsalucci Oct 4, 2022
ee10f5b
[EC-455] Imported Shared module to access i18n pipe // cleaned up str…
vincentsalucci Oct 5, 2022
42e3c0d
[EC-455] Adjusted padding for clear button // Changed hover color to …
vincentsalucci Oct 5, 2022
6a340b9
Merge branch 'master' into m/ng-select
vincentsalucci Oct 5, 2022
b98e4b9
[EC-455] FormObj factory
vincentsalucci Oct 5, 2022
a3bb47f
[EC-455] FormObj factory
vincentsalucci Oct 5, 2022
70c9cce
[EC-455] Updated FormFieldModule import/export statements
vincentsalucci Oct 5, 2022
ef25bce
[EC-455] Null check ngControl // added strings
vincentsalucci Oct 5, 2022
b5ac774
[EC-455] Fixed remaining null check // Added standalone story & input
vincentsalucci Oct 6, 2022
c0f6ba3
[EC-455] Actually adding the null check
vincentsalucci Oct 6, 2022
ab87a2c
Merge branch 'master' into m/ng-select
vincentsalucci Oct 6, 2022
57f86e0
[EC-455] Removed injector logic // Removed Value Accessor PROVIDER //…
vincentsalucci Oct 6, 2022
9412ca9
Merge branch 'master' into m/ng-select
vincentsalucci Oct 7, 2022
9d5b85d
Merge branch 'master' into m/ng-select
vincentsalucci Oct 10, 2022
67ad97a
[EC-455] Fixed ID copy pasta // Forwarded desribed by to focusable in…
vincentsalucci Oct 10, 2022
59e9136
[EC-455] Prettier
vincentsalucci Oct 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5390,5 +5390,17 @@
},
"numberOfUsers": {
"message": "Number of users"
},
"multiSelectPlaceholder": {
"message": "-- Type to Filter --"
},
"multiSelectLoading": {
"message": "Retrieving options..."
},
"multiSelectNotFound": {
"message": "No items found"
},
"multiSelectClearAll": {
"message": "Clear all"
}
}
7 changes: 7 additions & 0 deletions libs/components/src/form-field/form-field-control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export abstract class BitFormFieldControl {
ariaDescribedBy: string;
id: string;
required: boolean;
hasError: boolean;
error: [string, any];
}
5 changes: 2 additions & 3 deletions libs/components/src/form-field/form-field.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import {
ViewChild,
} from "@angular/core";

import { BitInputDirective } from "../input/input.directive";

import { BitErrorComponent } from "./error.component";
import { BitFormFieldControl } from "./form-field-control";
import { BitHintComponent } from "./hint.component";
import { BitPrefixDirective } from "./prefix.directive";
import { BitSuffixDirective } from "./suffix.directive";
Expand All @@ -22,7 +21,7 @@ import { BitSuffixDirective } from "./suffix.directive";
},
})
export class BitFormFieldComponent implements AfterContentChecked {
@ContentChild(BitInputDirective) input: BitInputDirective;
@ContentChild(BitFormFieldControl) input: BitFormFieldControl;
@ContentChild(BitHintComponent) hint: BitHintComponent;

@ViewChild(BitErrorComponent) error: BitErrorComponent;
Expand Down
7 changes: 5 additions & 2 deletions libs/components/src/form-field/form-field.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NgModule } from "@angular/core";

import { BitInputDirective } from "../input/input.directive";
import { InputModule } from "../input/input.module";
import { MultiSelectComponent } from "../multi-select/multi-select.component";
import { MultiSelectModule } from "../multi-select/multi-select.module";
import { SharedModule } from "../shared";

import { BitErrorSummary } from "./error-summary.component";
Expand All @@ -13,16 +15,17 @@ import { BitPrefixDirective } from "./prefix.directive";
import { BitSuffixDirective } from "./suffix.directive";

@NgModule({
imports: [SharedModule, InputModule],
imports: [SharedModule, InputModule, MultiSelectModule],
exports: [
BitErrorComponent,
BitErrorSummary,
BitFormFieldComponent,
BitHintComponent,
BitInputDirective,
BitLabel,
BitPrefixDirective,
BitSuffixDirective,
BitInputDirective,
MultiSelectComponent,
],
declarations: [
BitErrorComponent,
Expand Down
1 change: 1 addition & 0 deletions libs/components/src/form-field/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./form-field.module";
export * from "./form-field.component";
export * from "./form-field-control";
285 changes: 285 additions & 0 deletions libs/components/src/form-field/multi-select.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import {
FormsModule,
ReactiveFormsModule,
FormBuilder,
Validators,
FormGroup,
} from "@angular/forms";
import { NgSelectModule } from "@ng-select/ng-select";
import { action } from "@storybook/addon-actions";
import { Meta, moduleMetadata, Story } from "@storybook/angular";

import { I18nService } from "@bitwarden/common/abstractions/i18n.service";

import { BadgeModule } from "../badge";
import { ButtonModule } from "../button";
import { InputModule } from "../input/input.module";
import { MultiSelectComponent } from "../multi-select/multi-select.component";
import { SharedModule } from "../shared";
import { I18nMockService } from "../utils/i18n-mock.service";

import { FormFieldModule } from "./form-field.module";

export default {
title: "Component Library/Form/Multi Select",
excludeStories: /.*Data$/,
component: MultiSelectComponent,
decorators: [
moduleMetadata({
imports: [
ButtonModule,
FormsModule,
NgSelectModule,
FormFieldModule,
InputModule,
ReactiveFormsModule,
BadgeModule,
SharedModule,
],
providers: [
{
provide: I18nService,
useFactory: () => {
return new I18nMockService({
multiSelectPlaceholder: "-- Type to Filter --",
multiSelectLoading: "Retrieving options...",
multiSelectNotFound: "No items found",
multiSelectClearAll: "Clear all",
required: "required",
inputRequired: "Input is required.",
});
},
},
],
}),
],
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/f32LSg3jaegICkMu7rPARm/Tailwind-Component-Library-Update?node-id=1881%3A17689",
},
},
} as Meta;

export const actionsData = {
onItemsConfirmed: action("onItemsConfirmed"),
};

const fb = new FormBuilder();
const formObjFactory = () =>
fb.group({
select: [[], [Validators.required]],
});

function submit(formObj: FormGroup) {
formObj.markAllAsTouched();
}

const MultiSelectTemplate: Story<MultiSelectComponent> = (args: MultiSelectComponent) => ({
props: {
formObj: formObjFactory(),
submit: submit,
...args,
onItemsConfirmed: actionsData.onItemsConfirmed,
},
template: `
<form [formGroup]="formObj" (ngSubmit)="submit(formObj)">
<bit-form-field>
<bit-label>{{ name }}</bit-label>
<bit-multi-select
class="tw-w-full"
formControlName="select"
[baseItems]="baseItems"
[removeSelectedItems]="removeSelectedItems"
[loading]="loading"
[disabled]="disabled"
(onItemsConfirmed)="onItemsConfirmed($event)">
</bit-multi-select>
<bit-hint>{{ hint }}</bit-hint>
</bit-form-field>
<button type="submit" bitButton buttonType="primary">Submit</button>
</form>
`,
});

export const Loading = MultiSelectTemplate.bind({});
Loading.args = {
baseItems: [],
name: "Loading",
hint: "This is what a loading multi-select looks like",
loading: "true",
};

export const Disabled = MultiSelectTemplate.bind({});
Disabled.args = {
name: "Disabled",
disabled: "true",
hint: "This is what a disabled multi-select looks like",
};

export const Groups = MultiSelectTemplate.bind({});
Groups.args = {
name: "Select groups",
hint: "Groups will be assigned to the associated member",
baseItems: [
{ id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" },
{ id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" },
{ id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" },
{ id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" },
{ id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" },
{ id: "6", listName: "Group 6", labelName: "Group 6", icon: "bwi-family" },
{ id: "7", listName: "Group 7", labelName: "Group 7", icon: "bwi-family" },
],
};

export const Members = MultiSelectTemplate.bind({});
Members.args = {
name: "Select members",
hint: "Members will be assigned to the associated group/collection",
baseItems: [
{ id: "1", listName: "Joe Smith (jsmith@mail.me)", labelName: "Joe Smith", icon: "bwi-user" },
{
id: "2",
listName: "Tania Stone (tstone@mail.me)",
labelName: "Tania Stone",
icon: "bwi-user",
},
{
id: "3",
listName: "Matt Matters (mmatters@mail.me)",
labelName: "Matt Matters",
icon: "bwi-user",
},
{
id: "4",
listName: "Bob Robertson (brobertson@mail.me)",
labelName: "Bob Robertson",
icon: "bwi-user",
},
{
id: "5",
listName: "Ashley Fletcher (aflectcher@mail.me)",
labelName: "Ashley Fletcher",
icon: "bwi-user",
},
{ id: "6", listName: "Rita Olson (rolson@mail.me)", labelName: "Rita Olson", icon: "bwi-user" },
{
id: "7",
listName: "Final listName (fname@mail.me)",
labelName: "(fname@mail.me)",
icon: "bwi-user",
},
],
};

export const Collections = MultiSelectTemplate.bind({});
Collections.args = {
name: "Select collections",
hint: "Collections will be assigned to the associated member",
baseItems: [
{ id: "1", listName: "Collection 1", labelName: "Collection 1", icon: "bwi-collection" },
{ id: "2", listName: "Collection 2", labelName: "Collection 2", icon: "bwi-collection" },
{ id: "3", listName: "Collection 3", labelName: "Collection 3", icon: "bwi-collection" },
{
id: "3.5",
listName: "Child Collection 1 for Parent 1",
labelName: "Child Collection 1 for Parent 1",
icon: "bwi-collection",
parentGrouping: "Parent 1",
},
{
id: "3.55",
listName: "Child Collection 2 for Parent 1",
labelName: "Child Collection 2 for Parent 1",
icon: "bwi-collection",
parentGrouping: "Parent 1",
},
{
id: "3.59",
listName: "Child Collection 3 for Parent 1",
labelName: "Child Collection 3 for Parent 1",
icon: "bwi-collection",
parentGrouping: "Parent 1",
},
{
id: "3.75",
listName: "Child Collection 1 for Parent 2",
labelName: "Child Collection 1 for Parent 2",
icon: "bwi-collection",
parentGrouping: "Parent 2",
},
{ id: "4", listName: "Collection 4", labelName: "Collection 4", icon: "bwi-collection" },
{ id: "5", listName: "Collection 5", labelName: "Collection 5", icon: "bwi-collection" },
{ id: "6", listName: "Collection 6", labelName: "Collection 6", icon: "bwi-collection" },
{ id: "7", listName: "Collection 7", labelName: "Collection 7", icon: "bwi-collection" },
],
};

export const MembersAndGroups = MultiSelectTemplate.bind({});
MembersAndGroups.args = {
name: "Select groups and members",
hint: "Members/Groups will be assigned to the associated collection",
baseItems: [
{ id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" },
{ id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" },
{ id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" },
{ id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" },
{ id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" },
{ id: "6", listName: "Joe Smith (jsmith@mail.me)", labelName: "Joe Smith", icon: "bwi-user" },
{
id: "7",
listName: "Tania Stone (tstone@mail.me)",
labelName: "(tstone@mail.me)",
icon: "bwi-user",
},
],
};

export const RemoveSelected = MultiSelectTemplate.bind({});
RemoveSelected.args = {
name: "Select groups",
hint: "Groups will be removed from the list once the dropdown is closed",
baseItems: [
{ id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" },
{ id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" },
{ id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" },
{ id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" },
{ id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" },
{ id: "6", listName: "Group 6", labelName: "Group 6", icon: "bwi-family" },
{ id: "7", listName: "Group 7", labelName: "Group 7", icon: "bwi-family" },
],
removeSelectedItems: "true",
};

const StandaloneTemplate: Story<MultiSelectComponent> = (args: MultiSelectComponent) => ({
props: {
...args,
onItemsConfirmed: actionsData.onItemsConfirmed,
},
template: `
<bit-multi-select
class="tw-w-full"
[baseItems]="baseItems"
[removeSelectedItems]="removeSelectedItems"
[loading]="loading"
[disabled]="disabled"
[standalone]="standalone"
(onItemsConfirmed)="onItemsConfirmed($event)">
</bit-multi-select>
`,
});

export const Standalone = StandaloneTemplate.bind({});
Standalone.args = {
baseItems: [
{ id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" },
{ id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" },
{ id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" },
{ id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" },
{ id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" },
{ id: "6", listName: "Group 6", labelName: "Group 6", icon: "bwi-family" },
{ id: "7", listName: "Group 7", labelName: "Group 7", icon: "bwi-family" },
],
removeSelectedItems: "true",
standalone: "true",
};
5 changes: 4 additions & 1 deletion libs/components/src/input/input.directive.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Directive, HostBinding, Input, Optional, Self } from "@angular/core";
import { NgControl, Validators } from "@angular/forms";

import { BitFormFieldControl } from "../form-field/form-field-control";

// Increments for each instance of this component
let nextId = 0;

@Directive({
selector: "input[bitInput], select[bitInput], textarea[bitInput]",
providers: [{ provide: BitFormFieldControl, useExisting: BitInputDirective }],
})
export class BitInputDirective {
export class BitInputDirective implements BitFormFieldControl {
@HostBinding("class") @Input() get classList() {
return [
"tw-block",
Expand Down
7 changes: 7 additions & 0 deletions libs/components/src/multi-select/models/select-item-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type SelectItemView = {
id: string; // Unique ID used for comparisons
listName: string; // Default bindValue -> this is what will be displayed in list items
labelName: string; // This is what will be displayed in the selection option badge
icon: string; // Icon to display within the list
parentGrouping: string; // Used to group items by parent
};