Skip to content
This repository has been archived by the owner on Jan 16, 2024. It is now read-only.

Commit

Permalink
draft (#710)
Browse files Browse the repository at this point in the history
  • Loading branch information
DRITE committed Aug 29, 2022
1 parent 175af5a commit d6f4ee1
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"Enter value": "Enter value",
"Save info for developers": "Save info for developers",
"Show meta": "Show meta",
"Start date": "Start date",
"End date": "End date",
"Screen is missing or unavailable for your role": "Screen {{screenName}} is missing or unavailable for your role",
"View is missing or unavailable for your role": "View {{viewName}} is missing or unavailable for your role"
}
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"Refresh meta": "Обновить мета данные",
"Save info for developers": "Сохранить информацию для разработчиков",
"Show meta": "Показать мета данные",
"Start date": "Начальная дата",
"End date": "Конечная дата",
"Screen is missing or unavailable for your role": "Экран \"{{screenName}}\" отсутствует или недоступен для вашей роли",
"View is missing or unavailable for your role": "Панель \"{{viewName}}\" отсутствует или недоступна для вашей роли"
}
Expand Down
18 changes: 16 additions & 2 deletions src/components/ColumnTitle/ColumnFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export function ColumnFilter({ widgetName, widgetMeta, rowMeta, components }: Co
const bcName = widget?.bcName
const effectiveFieldMeta = (widget?.fields?.find((item: WidgetListField) => item.key === widgetMeta.filterBy) ??
widgetMeta) as WidgetListField
const widgetOptions = widget?.options
const filter = useSelector((store: Store) => store.screen.filters[bcName]?.find(item => item.fieldName === effectiveFieldMeta.key))
const [value, setValue] = React.useState(filter?.value)
const [visible, setVisible] = React.useState(false)
Expand Down Expand Up @@ -109,8 +110,21 @@ export function ColumnFilter({ widgetName, widgetMeta, rowMeta, components }: Co
}, [])

const content = components?.popup ?? (
<FilterPopup widgetName={widgetName} fieldKey={effectiveFieldMeta.key} value={value} onApply={handleApply} onCancel={handleClose}>
<FilterField widgetFieldMeta={effectiveFieldMeta} rowFieldMeta={rowMeta} value={value} onChange={setValue} />
<FilterPopup
widgetName={widgetName}
fieldKey={effectiveFieldMeta.key}
value={value}
onApply={handleApply}
onCancel={handleClose}
fieldType={effectiveFieldMeta.type}
>
<FilterField
widgetFieldMeta={effectiveFieldMeta}
rowFieldMeta={rowMeta}
value={value}
onChange={setValue}
widgetOptions={widgetOptions}
/>
</FilterPopup>
)

Expand Down
12 changes: 9 additions & 3 deletions src/components/FilterPopup/FilterPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,24 @@
*/

import React, { FormEvent } from 'react'
import { Form, Button } from 'antd'
import { Button, Form } from 'antd'
import styles from './FilterPopup.less'
import { BcFilter } from '../../interfaces/filters'
import { BcFilter, FilterType } from '../../interfaces/filters'
import { getFilterType } from '../../utils/filters'
import { useDispatch, useSelector } from 'react-redux'
import { $do } from '../../actions/actions'
import { Store } from '../../interfaces/store'
import { WidgetField } from '../../interfaces/widget'
import { DataValue } from '../../interfaces/data'
import { useTranslation } from 'react-i18next'
import { FieldType } from '@tesler-ui/schema'

export interface FilterPopupProps {
widgetName: string
fieldKey: string
value: DataValue | DataValue[]
children: React.ReactNode
fieldType?: FieldType
onApply?: () => void
onCancel?: () => void
}
Expand Down Expand Up @@ -64,7 +66,11 @@ export const FilterPopup: React.FC<FilterPopupProps> = props => {
const handleApply = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
const newFilter: BcFilter = {
type: getFilterType(widgetMeta.type),
type:
widget.options?.filterDateByRange &&
[FieldType.date, FieldType.dateTime, FieldType.dateTimeWithSeconds].includes(props.fieldType) // todo the list must be extendable by customer
? FilterType.range
: getFilterType(widgetMeta.type),
value: props.value,
fieldName: props.fieldKey,
viewName,
Expand Down
7 changes: 6 additions & 1 deletion src/components/ui/FilterField/FilterField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ import { FieldType } from '../../../interfaces/view'
import { Checkbox, Input, Icon, DatePicker } from 'antd'
import { CheckboxChangeEvent } from 'antd/lib/checkbox'
import moment, { Moment } from 'moment'
import { WidgetListField } from '../../../interfaces/widget'
import { WidgetListField, WidgetMeta } from '../../../interfaces/widget'
import { RowMetaField } from '../../../interfaces/rowMeta'
import { getFormat } from '../DatePickerField/DatePickerField'
import RangePicker from './components/RangePicker'

export interface ColumnFilterControlProps {
widgetFieldMeta: WidgetListField
rowFieldMeta: RowMetaField
value: DataValue | DataValue[]
onChange: (value: DataValue | DataValue[]) => void
widgetOptions?: WidgetMeta['options']
}

/**
Expand Down Expand Up @@ -66,6 +68,9 @@ export const ColumnFilterControl: React.FC<ColumnFilterControlProps> = props =>
}
case FieldType.dateTime:
case FieldType.date: {
if (props.widgetOptions?.filterDateByRange) {
return <RangePicker value={props.value as DataValue[]} onChange={v => props.onChange(v)} format={getFormat()} />
}
return (
<DatePicker
autoFocus
Expand Down
5 changes: 5 additions & 0 deletions src/components/ui/FilterField/components/RangePicker.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.container {
:global(.ant-calendar-picker:first-child) {
margin-right: 8px;
}
}
71 changes: 71 additions & 0 deletions src/components/ui/FilterField/components/RangePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react'
import { DatePicker } from 'antd'
import { DatePickerProps } from 'antd/lib/date-picker/interface'
import { DataValue } from '@tesler-ui/schema'
import moment, { Moment } from 'moment'
import styles from './RangePicker.less'
import { useTranslation } from 'react-i18next'

interface RangePickerProps extends Omit<DatePickerProps, 'onChange' | 'value'> {
onChange: (v: DataValue[]) => void
value: DataValue[]
}

function RangePicker({ value, onChange, ...rest }: RangePickerProps) {
const startDate = Array.isArray(value) && value?.[0] ? moment(value[0] as string, moment.ISO_8601) : null
const endDate = Array.isArray(value) && value?.[1] ? moment(value[1] as string, moment.ISO_8601) : null
const [endOpen, setEndOpen] = useState(false)

const disabledStartDate = (startValue: Moment) => {
if (!startValue || !endDate) {
return false
}
return startValue.valueOf() > endDate.valueOf()
}

const disabledEndDate = (endValue: Moment) => {
if (!endValue || !startDate) {
return false
}
return endValue.valueOf() <= startDate.valueOf()
}

const handleStartOpenChange = (open: boolean) => {
if (!open) {
setEndOpen(true)
}
}

const handleEndOpenChange = (open: boolean) => {
setEndOpen(open)
}

const { t } = useTranslation()
return (
<div className={styles.container}>
<DatePicker
{...rest}
placeholder={t('Start date')}
disabledDate={disabledStartDate}
onChange={(date: Moment, dateString: string) => {
onChange([date?.toISOString(), endDate?.toISOString()])
}}
value={startDate}
onOpenChange={handleStartOpenChange}
/>
<DatePicker
{...rest}
placeholder={t('End date')}
disabledDate={disabledEndDate}
onChange={(date: Moment, dateString: string) => {
onChange([startDate?.toISOString(), date?.toISOString()])
}}
value={endDate}
open={endOpen}
onOpenChange={handleEndOpenChange}
/>
</div>
)
}

export default React.memo(RangePicker)
4 changes: 4 additions & 0 deletions src/interfaces/filters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { DataValue } from './data'

export enum FilterType {
/**
* Transforms into combination of 'greaterOrEqualThan' and 'lessOrEqualThan' (See src/utils/filters.ts)
*/
range = 'range',
equals = 'equals',
greaterThan = 'greaterThan',
lessThan = 'lessThan',
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export interface WidgetMeta {
limit?: number
gridWidth: number // 1-24
fields: unknown[]
options?: WidgetOptions
options?: WidgetOptions & { filterDateByRange?: boolean } // TODO extract `filterDateByRange` to tesler-schema
showCondition?: WidgetShowCondition
description?: string // description for documentation
}
Expand Down
14 changes: 13 additions & 1 deletion src/utils/__tests__/filters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { getFilters, getSorters, parseSorters, getFilterType, parseFilters } from '../filters'
import { getFilters, getFilterType, getSorters, parseFilters, parseSorters } from '../filters'
import { FieldType } from '../../interfaces/view'
import { FilterType } from '../../interfaces/filters'

Expand Down Expand Up @@ -70,6 +70,18 @@ describe('getFilters', () => {
expect(getFilters(null)).toBe(null)
expect(getFilters([])).toBe(null)
})
it("should convert \"range\" into combination of 'greaterOrEqualThan' and 'lessOrEqualThan'", () => {
expect(getFilters([{ type: FilterType.range, fieldName: 'test-field', value: [1, 2] }])).toMatchObject({
'test-field.greaterOrEqualThan': '1',
'test-field.lessOrEqualThan': '2'
})
expect(getFilters([{ type: FilterType.range, fieldName: 'test-field', value: [1, null] }])).toMatchObject({
'test-field.greaterOrEqualThan': '1'
})
expect(getFilters([{ type: FilterType.range, fieldName: 'test-field', value: [null, 2] }])).toMatchObject({
'test-field.lessOrEqualThan': '2'
})
})
})

describe('getSorters', () => {
Expand Down
20 changes: 15 additions & 5 deletions src/utils/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ export function getFilters(filters: BcFilter[]) {
}
const result: Record<string, string> = {}
filters.forEach(item => {
let value = String(item.value)
if (Array.isArray(item.value)) {
const values = (item.value as DataValue[]).map(val => `"${val}"`)
value = `[${values}]`
if (item.type === FilterType.range) {
const values = item.value as DataValue[]
if (values[0]) {
result[`${item.fieldName}.${FilterType.greaterOrEqualThan}`] = String(values[0])
}
if (values[1]) {
result[`${item.fieldName}.${FilterType.lessOrEqualThan}`] = String(values[1])
}
} else {
let value = String(item.value)
if (Array.isArray(item.value)) {
const values = (item.value as DataValue[]).map(val => `"${val}"`)
value = `[${values}]`
}
result[`${item.fieldName}.${item.type}`] = value
}
result[`${item.fieldName}.${item.type}`] = value
})
return result
}
Expand Down

0 comments on commit d6f4ee1

Please sign in to comment.