From 7d28542fe50703b418f306152bb1043f4566a1d1 Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Tue, 1 Nov 2022 14:18:50 -0400 Subject: [PATCH 1/4] Make CW Dropdown searchable --- .../components/component_kit/cw_dropdown.tsx | 101 ++++++++++++++---- .../views/components/component_kit/types.ts | 47 ++++---- .../components/component_kit/cw_dropdown.scss | 4 +- 3 files changed, 113 insertions(+), 39 deletions(-) diff --git a/packages/commonwealth/client/scripts/views/components/component_kit/cw_dropdown.tsx b/packages/commonwealth/client/scripts/views/components/component_kit/cw_dropdown.tsx index 94725126407..f2305fef864 100644 --- a/packages/commonwealth/client/scripts/views/components/component_kit/cw_dropdown.tsx +++ b/packages/commonwealth/client/scripts/views/components/component_kit/cw_dropdown.tsx @@ -1,55 +1,118 @@ /* @jsx m */ -import m from 'mithril'; +import m, { VnodeDOM } from 'mithril'; import 'components/component_kit/cw_dropdown.scss'; -import { CWTextInput } from './cw_text_input'; +import { CWTextInput, TextInputAttrs } from './cw_text_input'; import { CWPopoverMenuItem } from './cw_popover/cw_popover_menu'; -import { MenuItem } from './types'; +import { DefaultMenuItem } from './types'; +import { ValidationStatus } from './cw_validation_text'; export type DropdownInputAttrs = { - inputOptions: Array; + customFilter: (item: DefaultMenuItem, query: string) => DefaultMenuItem[]; + defaultActiveIndex?: number; + defaultMenuItems: DefaultMenuItem[]; + inputValidationFn?: (value: string) => [ValidationStatus, string]; + label: string; + placeholder?: string; onSelect?: (optionLabel: string, index?: number) => void; - initialValue?: string; + searchable?: boolean; + textInputAttrs?: TextInputAttrs; }; export class CWDropdown implements m.ClassComponent { private showDropdown: boolean; - private selectedValue: string; + private activeMenuItems: DefaultMenuItem[]; + private value: string; - oninit(vnode) { + filterMenuItems( + items: DefaultMenuItem[], + query: string, + customFilter?: (item: DefaultMenuItem, query: string) => DefaultMenuItem[] + ) { + const defaultFilter = (item: DefaultMenuItem) => { + return item.label.toLowerCase().includes(query.toLowerCase()); + }; + const filterFn = customFilter + ? (item: DefaultMenuItem) => customFilter(item, query) + : defaultFilter; + + return items.filter(filterFn); + } + + oninit(vnode: VnodeDOM) { this.showDropdown = false; - this.selectedValue = - vnode.attrs.initialValue ?? vnode.attrs.inputOptions[0].label; + this.activeMenuItems = vnode.attrs.defaultMenuItems; + + document.body.addEventListener('click', (event) => { + const $dropdown = document.querySelector('.dropdown-wrapper'); + if (!$dropdown) return; + if (!$dropdown.contains(event.target as Node)) { + this.showDropdown = false; + m.redraw(); + } + }); } - view(vnode) { - const { inputOptions, onSelect } = vnode.attrs; + view(vnode: VnodeDOM) { + const { + customFilter, + defaultActiveIndex, + defaultMenuItems, + inputValidationFn, + label, + onSelect, + placeholder, + searchable, + } = vnode.attrs; + const { activeMenuItems, showDropdown, ...value } = this; return (