From c6488be581bcd6f93a1ef71e9ad16ebc7fa981a7 Mon Sep 17 00:00:00 2001 From: marcelines Date: Tue, 6 Jun 2023 17:49:23 +0100 Subject: [PATCH] feat: add filters with checkboxes --- .../filters/components/color-circle.tsx | 44 +++++++ .../table-issues/filters/filter-author.tsx | 79 ----------- .../filters/filter-with-checkboxes.tsx | 105 +++++++++++++++ .../components/table-issues/filters/index.ts | 2 + .../components/table-issues/filters/tabs.tsx | 41 ++++++ .../components/table-issues/table-issues.tsx | 124 ++++++++++-------- .../src/pages/insights/epics/[epic].tsx | 7 +- .../src/dropdown-menu/dropdown-menu.tsx | 2 +- packages/components/src/tag/tag.stories.tsx | 2 +- 9 files changed, 265 insertions(+), 141 deletions(-) create mode 100644 apps/website/src/components/table-issues/filters/components/color-circle.tsx delete mode 100644 apps/website/src/components/table-issues/filters/filter-author.tsx create mode 100644 apps/website/src/components/table-issues/filters/filter-with-checkboxes.tsx create mode 100644 apps/website/src/components/table-issues/filters/index.ts create mode 100644 apps/website/src/components/table-issues/filters/tabs.tsx diff --git a/apps/website/src/components/table-issues/filters/components/color-circle.tsx b/apps/website/src/components/table-issues/filters/components/color-circle.tsx new file mode 100644 index 00000000..d0cabe46 --- /dev/null +++ b/apps/website/src/components/table-issues/filters/components/color-circle.tsx @@ -0,0 +1,44 @@ +import { tokens } from '@status-im/components/src/tokens' + +import type { ColorTokens } from '@tamagui/core' + +// TypeGuard for ColorTokens +function isColorTokens( + value: `#${string}` | ColorTokens +): value is ColorTokens { + return typeof value === 'string' && value.startsWith('$') +} + +const ColorCircle = ({ + color: colorFromProps, +}: { + color?: ColorTokens | `#${string}` +}) => { + if (!colorFromProps) { + return null + } + + let color: ColorTokens | string = colorFromProps + + if (isColorTokens(colorFromProps)) { + const colorToken = colorFromProps.replace( + '$', + '' + ) as keyof typeof tokens.color + color = tokens.color[colorToken]?.val || colorFromProps + } + + return ( +
+ ) +} + +export { ColorCircle } diff --git a/apps/website/src/components/table-issues/filters/filter-author.tsx b/apps/website/src/components/table-issues/filters/filter-author.tsx deleted file mode 100644 index c0c3ffc8..00000000 --- a/apps/website/src/components/table-issues/filters/filter-author.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useState } from 'react' - -import { Avatar, Button, Input, Text } from '@status-im/components' -import { DropdownMenu } from '@status-im/components/src/dropdown-menu' -import { DropdownIcon, SearchIcon } from '@status-im/icons' - -type Props = { - authors: { - id: number - name: string - avatar?: string - }[] -} -const FilterAuthor = (props: Props) => { - const { authors } = props - - const [authorFilterText, setAuthorFilterText] = useState('') - const filteredAuthors = authors.filter(author => - author.name.toLowerCase().includes(authorFilterText.toLowerCase()) - ) - - const [selectedAuthors, setSelectedAuthors] = useState([]) - - return ( -
- - - -
- } - size={32} - value={authorFilterText} - onChangeText={setAuthorFilterText} - /> -
- {filteredAuthors.map(author => ( - - } - label={author.name} - checked={selectedAuthors.includes(author.id)} - onSelect={() => { - if (selectedAuthors.includes(author.id)) { - setSelectedAuthors( - selectedAuthors.filter(id => id !== author.id) - ) - } else { - setSelectedAuthors([...selectedAuthors, author.id]) - } - }} - /> - ))} - {filteredAuthors.length === 0 && ( -
- No authors found -
- )} -
-
-
- ) -} - -export { FilterAuthor } diff --git a/apps/website/src/components/table-issues/filters/filter-with-checkboxes.tsx b/apps/website/src/components/table-issues/filters/filter-with-checkboxes.tsx new file mode 100644 index 00000000..de74a2ef --- /dev/null +++ b/apps/website/src/components/table-issues/filters/filter-with-checkboxes.tsx @@ -0,0 +1,105 @@ +import { useState } from 'react' + +import { Avatar, Button, Input, Text } from '@status-im/components' +import { DropdownMenu } from '@status-im/components/src/dropdown-menu' +import { DropdownIcon, SearchIcon } from '@status-im/icons' + +import { ColorCircle } from './components/color-circle' + +import type { ColorTokens } from '@tamagui/core' + +type Props = { + data: { + id: number + name: string + avatar?: string + color?: ColorTokens | `#${string}` + }[] + label: string + noResultsText?: string + noPadding?: boolean +} +const FilterWithCheckboxes = (props: Props) => { + const { data, label, noResultsText, noPadding } = props + + const [filterText, setFilterText] = useState('') + + // TODO - this will be improved by having a debounced search and use memoization + const filteredData = data.filter(label => + label.name.toLowerCase().includes(filterText.toLowerCase()) + ) + + const [selectedValues, setSelectedValues] = useState([]) + const [isOpen, setIsOpen] = useState(false) + + return ( +
+ setIsOpen(!isOpen)}> +
+ } + > + {label} + + +
+ } + size={32} + value={filterText} + onChangeText={setFilterText} + /> +
+ {filteredData.map(filtered => { + return ( + + ) : ( + + ) + } + label={filtered.name} + checked={selectedValues.includes(filtered.id)} + onSelect={() => { + if (selectedValues.includes(filtered.id)) { + setSelectedValues( + selectedValues.filter(id => id !== filtered.id) + ) + } else { + setSelectedValues([...selectedValues, filtered.id]) + } + }} + /> + ) + })} + {filteredData.length === 0 && ( +
+ {noResultsText} +
+ )} +
+ +
+ ) +} + +export { FilterWithCheckboxes } +export type { Props as FilterWithCheckboxesProps } diff --git a/apps/website/src/components/table-issues/filters/index.ts b/apps/website/src/components/table-issues/filters/index.ts new file mode 100644 index 00000000..40cc2f58 --- /dev/null +++ b/apps/website/src/components/table-issues/filters/index.ts @@ -0,0 +1,2 @@ +export { FilterWithCheckboxes } from './filter-with-checkboxes' +export { Tabs } from './tabs' diff --git a/apps/website/src/components/table-issues/filters/tabs.tsx b/apps/website/src/components/table-issues/filters/tabs.tsx new file mode 100644 index 00000000..c90037b7 --- /dev/null +++ b/apps/website/src/components/table-issues/filters/tabs.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react' + +import { DoneIcon, OpenIcon } from '@status-im/icons' + +const Tabs = (): JSX.Element => { + const [activeTab, setActiveTab] = useState<'open' | 'closed'>('open') + + const isOpen = activeTab === 'open' + + return ( +
+
+ +
+
+ +
+
+ ) +} + +export { Tabs } diff --git a/apps/website/src/components/table-issues/table-issues.tsx b/apps/website/src/components/table-issues/table-issues.tsx index 5266442c..737b04ae 100644 --- a/apps/website/src/components/table-issues/table-issues.tsx +++ b/apps/website/src/components/table-issues/table-issues.tsx @@ -1,11 +1,12 @@ import { useRef, useState } from 'react' import { Avatar, Button, Input, Tag, Text } from '@status-im/components' -import { OpenIcon, SearchIcon } from '@status-im/icons' +import { SearchIcon } from '@status-im/icons' import Link from 'next/link' -import { FilterAuthor } from './filters/filter-author' +import { FilterWithCheckboxes, Tabs } from './filters' +import type { FilterWithCheckboxesProps } from './filters/filter-with-checkboxes' import type { TextInput } from 'react-native' const issues = [ @@ -51,25 +52,7 @@ const issues = [ }, ] -const authors = [ - { - id: 1, - name: 'Tobias', - avatar: - 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80', - }, - { - id: 2, - name: 'Arnold', - avatar: - 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80', - }, - { - id: 3, - name: 'Alisher', - avatar: - 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80', - }, +const authors: FilterWithCheckboxesProps['data'] = [ { id: 4, name: 'marcelines', @@ -92,9 +75,53 @@ const authors = [ }, ] -export const TableIssues = () => { - const [activeTab, setActiveTab] = useState<'open' | 'closed'>('open') +const epics: FilterWithCheckboxesProps['data'] = [ + { + id: 4, + name: 'E:ActivityCenter', + color: '$orange-60', + }, + { + id: 5, + name: 'E:Keycard', + color: '$purple-60', + }, + { + id: 6, + name: 'E:Wallet', + color: '$pink-60', + }, + { + id: 7, + name: 'E:Chat', + color: '$beige-60', + }, +] +const labels: FilterWithCheckboxesProps['data'] = [ + { + id: 4, + name: 'Mobile', + color: '$blue-60', + }, + { + id: 5, + name: 'Frontend', + color: '$brown-50', + }, + { + id: 6, + name: 'Backend', + color: '$red-60', + }, + { + id: 7, + name: 'Desktop', + color: '$green-60', + }, +] + +export const TableIssues = () => { const [issuesSearchText, setIssuesSearchText] = useState('') const inputRef = useRef(null) const [isMinimized, setIsMinimized] = useState(true) @@ -102,30 +129,10 @@ export const TableIssues = () => { return (
-
-
- -
-
- -
-
+
-
+
} @@ -138,17 +145,22 @@ export const TableIssues = () => { ref={inputRef} />
- -
- -
-
- -
+ + +
diff --git a/apps/website/src/pages/insights/epics/[epic].tsx b/apps/website/src/pages/insights/epics/[epic].tsx index 09a66fd0..80eaf823 100644 --- a/apps/website/src/pages/insights/epics/[epic].tsx +++ b/apps/website/src/pages/insights/epics/[epic].tsx @@ -9,15 +9,14 @@ const EpicsDetailPage: Page = () => {
-
+
- -
- +
+
diff --git a/packages/components/src/dropdown-menu/dropdown-menu.tsx b/packages/components/src/dropdown-menu/dropdown-menu.tsx index 1fc4990f..32c4f454 100644 --- a/packages/components/src/dropdown-menu/dropdown-menu.tsx +++ b/packages/components/src/dropdown-menu/dropdown-menu.tsx @@ -79,7 +79,7 @@ const DropdownMenuCheckboxItem = forwardRef< return ( - {cloneElement(icon, { color: '$neutral-50' })} + {cloneElement(icon)} {label} diff --git a/packages/components/src/tag/tag.stories.tsx b/packages/components/src/tag/tag.stories.tsx index 1bd16fcd..6ddc331a 100644 --- a/packages/components/src/tag/tag.stories.tsx +++ b/packages/components/src/tag/tag.stories.tsx @@ -42,7 +42,7 @@ export const Default: Story = { - +