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

feat: GenericSelectionListFilter #355

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fe4bdfa
feat(datastore): add hook for accessing datastore
Birkbjo Aug 17, 2023
f53db76
fix(types): fix types for section handle
Birkbjo Aug 17, 2023
6231edb
feat(constants): add constants for columns and translated properties
Birkbjo Aug 18, 2023
29dad8a
fix: update types for sectionlistcolumns
Birkbjo Aug 18, 2023
5702039
fix: add selector to usedatastore
Birkbjo Aug 18, 2023
3156dd1
feat(managecolumns): manage columns implementation
Birkbjo Aug 18, 2023
8a08d34
fix: integrate selectedcoluns with list
Birkbjo Aug 18, 2023
fd3b02c
fix: mergecolumns
Birkbjo Aug 18, 2023
a7c1c6c
fix: managecolumns style
Birkbjo Aug 19, 2023
8ee8c0a
refactor: managecolumns
Birkbjo Aug 21, 2023
38f22e6
chore: add remeda util lib
Birkbjo Aug 22, 2023
befd522
refactor: refactor to config for listviews
Birkbjo Aug 22, 2023
e799d2b
refactor: manage list view
Birkbjo Sep 5, 2023
fbb1a20
fix: respect order of selected columns
Birkbjo Sep 6, 2023
d3c4625
fix(datastore): always use put for datastore
Birkbjo Sep 6, 2023
c39fd99
Merge branch 'master' into DHIS2-15562/manage-columns
Birkbjo Sep 12, 2023
14c7c05
refactor: allow arbitrary path as column
Birkbjo Sep 14, 2023
4858135
refactor: remove remeda, implement uniqueBy
Birkbjo Sep 14, 2023
5df49bd
fix: add uniqueBy
Birkbjo Sep 14, 2023
dbc4f82
fix: add test for path
Birkbjo Sep 14, 2023
0f6efc0
fix: add maxDepth to getFieldFilterFromPath
Birkbjo Sep 14, 2023
a88586e
fix: fix some tests
Birkbjo Sep 14, 2023
85dff70
fix: remove typepath file
Birkbjo Sep 14, 2023
2d07750
refactor: cleanup
Birkbjo Sep 26, 2023
099993e
feat: generic selectionlist filter
Birkbjo Sep 27, 2023
f84db2b
refactor: some cleanup
Birkbjo Sep 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare module '@dhis2/d2-i18n' {
const language: string
export function t(key: string, options?: any): string
export function exists(key: string): boolean
}

declare module '@dhis2/ui'
Expand Down
80 changes: 67 additions & 13 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-09-05T12:02:44.326Z\n"
"PO-Revision-Date: 2023-09-05T12:02:44.326Z\n"
"POT-Creation-Date: 2023-09-12T11:17:52.981Z\n"
"PO-Revision-Date: 2023-09-12T11:17:52.981Z\n"

msgid "schemas"
msgstr "schemas"
Expand Down Expand Up @@ -48,6 +48,12 @@ msgstr "Search for menu items"
msgid "Retry"
msgstr "Retry"

msgid "All"
msgstr "All"

msgid "Category combo"
msgstr "Category combo"

msgid "Failed to load {{label}}"
msgstr "Failed to load {{label}}"

Expand Down Expand Up @@ -87,12 +93,33 @@ msgstr "Type to filter options"
msgid "No matches"
msgstr "No matches"

msgid "Data set"
msgstr "Data set"

msgid "Clear all filters"
msgstr "Clear all filters"

msgid "Search by name, code or ID"
msgstr "Search by name, code or ID"

msgid "Available table columns"
msgstr "Available table columns"

msgid "Selected table columns"
msgstr "Selected table columns"

msgid "Reset to default columns"
msgstr "Reset to default columns"

msgid "Manage {{section}} table columns"
msgstr "Manage {{section}} table columns"

msgid "Cancel"
msgstr "Cancel"

msgid "Update table columns"
msgstr "Update table columns"

msgid "Public can edit"
msgstr "Public can edit"

Expand Down Expand Up @@ -156,9 +183,6 @@ msgstr "Data element group set"
msgid "Data element group sets"
msgstr "Data element group sets"

msgid "Data set"
msgstr "Data set"

msgid "Data sets"
msgstr "Data sets"

Expand Down Expand Up @@ -501,20 +525,50 @@ msgstr "Image"
msgid "GeoJSON"
msgstr "GeoJSON"

msgid "Name"
msgstr "Name"
msgid "Code"
msgstr "Code"

msgid "Created by"
msgstr "Created by"

msgid "Favorite"
msgstr "Favorite"

msgid "Href"
msgstr "Href"

msgid "Id"
msgstr "Id"

msgid "Last updated by"
msgstr "Last updated by"

msgid "Domain"
msgstr "Domain"
msgid "Created"
msgstr "Created"

msgid "Value"
msgstr "Value"
msgid "Domain type"
msgstr "Domain type"

msgid "Last updated"
msgstr "Last updated"

msgid "Public access"
msgstr "Public access"
msgid "Name"
msgstr "Name"

msgid "Sharing"
msgstr "Sharing"

msgid "Short name"
msgstr "Short name"

msgid "Value type"
msgstr "Value type"

msgid "Owner"
msgstr "Owner"

msgid "Zero is significant"
msgstr "Zero is significant"

msgid "Metadata management"
msgstr "Metadata management"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"react-router-dom": "^6.11.2",
"use-debounce": "^9.0.4",
"use-query-params": "^2.2.1",
"zod": "^3.22.2",
"zustand": "^4.4.0"
}
}
5 changes: 2 additions & 3 deletions src/app/routes/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { useMatches } from 'react-router-dom'
import type { SchemaSection } from '../../constants/sections'

import type { ModelSection } from '../../types'
// utility type to type a match with a handle-property returned from useMatches
// since handle is unknown, we need to cast it to the correct type
type MatchWithHandle<THandle> = ReturnType<typeof useMatches>[number] & {
Expand All @@ -10,7 +9,7 @@ type MatchWithHandle<THandle> = ReturnType<typeof useMatches>[number] & {
// common type for possible handle-properties used in Route
export type RouteHandle = {
hideSidebar?: boolean
section?: SchemaSection
section?: ModelSection
crumb?: () => React.ReactNode
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ interface SearchableSingleSelectPropTypes {
selected?: string
error?: string
showAllOption?: boolean
label: string
}

export const SearchableSingleSelect = ({
showAllOption,
error,
label,
loading,
onChange,
onFilterChange,
Expand Down Expand Up @@ -96,7 +98,6 @@ export const SearchableSingleSelect = ({
const observer = new IntersectionObserver(
(entries) => {
const [{ isIntersecting }] = entries

if (isIntersecting) {
onEndReached()
}
Expand Down Expand Up @@ -124,7 +125,7 @@ export const SearchableSingleSelect = ({
// any value to the "selected" prop, as otherwise an error will be thrown
selected={hasSelectedInOptionList ? selected : ''}
onChange={onChange}
placeholder={i18n.t('Category combo')}
placeholder={label}
>
<div className={classes.searchField}>
<div className={classes.searchInput}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import i18n from '@dhis2/d2-i18n'
import React, { useCallback, useRef, useState } from 'react'
import { SelectOption } from '../../../types'
import { SearchableSingleSelect } from '../../SearchableSingleSelect'
Expand Down Expand Up @@ -92,6 +93,7 @@ export function CategoryComboSelect({

return (
<SearchableSingleSelect
label={i18n.t('Category combo')}
showAllOption={showAllOption}
onChange={({ selected }) => {
if (selected === selectedOption?.value) {
Expand Down
37 changes: 22 additions & 15 deletions src/components/sectionList/SectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@ import {
} from '@dhis2/ui'
import React, { PropsWithChildren } from 'react'
import { CheckBoxOnChangeObject } from '../../types'
import { IdentifiableObject } from '../../types/generated'
import { SelectedColumns } from './types'

type SectionListProps<Model extends IdentifiableObject> = {
headerColumns: SelectedColumns<Model>
type SectionListProps = {
headerColumns: SelectedColumns
onSelectAll: (checked: boolean) => void
allSelected?: boolean
}

export const SectionList = <Model extends IdentifiableObject>({
export const SectionList = ({
allSelected,
headerColumns,
children,
onSelectAll,
}: PropsWithChildren<SectionListProps<Model>>) => {
}: PropsWithChildren<SectionListProps>) => {
return (
<DataTable>
<TableHead>
Expand All @@ -37,19 +36,27 @@ export const SectionList = <Model extends IdentifiableObject>({
}
/>
</DataTableColumnHeader>
{headerColumns.map((headerColumn) => (
<DataTableColumnHeader
key={headerColumn.modelPropertyName}
>
{headerColumn.label}
</DataTableColumnHeader>
))}
<DataTableColumnHeader>
{i18n.t('Actions')}
</DataTableColumnHeader>
{headerColumns.length > 0 && (
<HeaderColumns headerColumns={headerColumns} />
)}
</DataTableRow>
</TableHead>
<TableBody>{children}</TableBody>
</DataTable>
)
}

const HeaderColumns = ({
headerColumns,
}: {
headerColumns: SelectedColumns
}) => (
<>
{headerColumns.map((headerColumn) => (
<DataTableColumnHeader key={headerColumn.path}>
{headerColumn.label}
</DataTableColumnHeader>
))}
<DataTableColumnHeader>{i18n.t('Actions')}</DataTableColumnHeader>
</>
)
19 changes: 6 additions & 13 deletions src/components/sectionList/SectionListRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ import { SelectedColumns, SelectedColumn } from './types'

export type SectionListRowProps<Model extends IdentifiableObject> = {
modelData: GistModel<Model>
selectedColumns: SelectedColumns<Model>
selectedColumns: SelectedColumns
onSelect: (modelId: string, checked: boolean) => void
selected: boolean
renderValue: (
column: SelectedColumn<Model>['modelPropertyName'],
value: GistModel<Model>[typeof column]
) => React.ReactNode
renderColumnValue: (column: SelectedColumn) => React.ReactNode
}

export function SectionListRow<Model extends IdentifiableObject>({
selectedColumns,
modelData,
onSelect,
selected,
renderValue,
renderColumnValue,
}: SectionListRowProps<Model>) {
return (
<DataTableRow
Expand All @@ -39,13 +36,9 @@ export function SectionListRow<Model extends IdentifiableObject>({
}}
/>
</DataTableCell>
{selectedColumns.map(({ modelPropertyName }) => (
<DataTableCell key={modelPropertyName}>
{modelData[modelPropertyName] &&
renderValue(
modelPropertyName,
modelData[modelPropertyName]
)}
{selectedColumns.map((selectedColumn) => (
<DataTableCell key={selectedColumn.path}>
{renderColumnValue(selectedColumn)}
</DataTableCell>
))}
<DataTableCell>
Expand Down
25 changes: 13 additions & 12 deletions src/components/sectionList/SectionListWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useMemo, useState } from 'react'
import { useSchemaFromHandle } from '../../lib'
import { IdentifiableObject, GistCollectionResponse } from '../../types/models'
import { FilterWrapper } from './filters/FilterWrapper'
import { useModelListView } from './listView'
import { ModelValue } from './modelValue/ModelValue'
import { SectionList } from './SectionList'
import { SectionListLoader } from './SectionListLoader'
Expand All @@ -11,25 +12,20 @@ import { SectionListPagination } from './SectionListPagination'
import { SectionListRow } from './SectionListRow'
import { SectionListTitle } from './SectionListTitle'
import { SelectionListHeader } from './SelectionListHeaderNormal'
import { SelectedColumns } from './types'

type SectionListWrapperProps<Model extends IdentifiableObject> = {
availableColumns?: SelectedColumns<Model>
defaultColumns: SelectedColumns<Model>
filterElement?: React.ReactElement
data: GistCollectionResponse<Model> | undefined
error: FetchError | undefined
}

export const SectionListWrapper = <Model extends IdentifiableObject>({
availableColumns,
defaultColumns,
filterElement,
data,
error,
}: SectionListWrapperProps<Model>) => {
const { columns: headerColumns } = useModelListView()
const schema = useSchemaFromHandle()
const [selectedColumns, setSelectedColumns] =
useState<SelectedColumns<Model>>(defaultColumns)
const [selectedModels, setSelectedModels] = useState<Set<string>>(new Set())

const handleSelect = (id: string, checked: boolean) => {
Expand Down Expand Up @@ -82,7 +78,7 @@ export const SectionListWrapper = <Model extends IdentifiableObject>({
<FilterWrapper>{filterElement}</FilterWrapper>
<SelectionListHeader />
<SectionList
headerColumns={selectedColumns}
headerColumns={headerColumns}
onSelectAll={handleSelectAll}
allSelected={allSelected}
>
Expand All @@ -91,15 +87,20 @@ export const SectionListWrapper = <Model extends IdentifiableObject>({
<SectionListRow
key={model.id}
modelData={model}
selectedColumns={selectedColumns}
selectedColumns={headerColumns}
onSelect={handleSelect}
selected={selectedModels.has(model.id)}
renderValue={(modelPropertyName, value) => {
renderColumnValue={({ path }) => {
return (
<ModelValue
modelPropertyName={modelPropertyName}
path={path}
schema={schema}
value={value}
component={
headerColumns.find(
(c) => c.path === path
)?.component
}
model={model}
/>
)
}}
Expand Down
Loading
Loading