Skip to content

Commit

Permalink
fix custom date filter
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperkristensen committed Nov 12, 2024
1 parent b337773 commit 283c15a
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export const DataTable = <TData,>({
const enableCommands = commands && commands.length > 0
const enableSorting = columns.some((column) => column.enableSorting)

const filterIds = filters?.map((f) => getQueryParamKey(f.id, prefix)) ?? []
const filterIds = filters?.map((f) => f.id) ?? []
const prefixedFilterIds = filterIds.map((id) => getQueryParamKey(id, prefix))

const { offset, order, q, ...filterParams } = useQueryParams(
[
Expand Down Expand Up @@ -145,13 +146,16 @@ export const DataTable = <TData,>({

setSearchParams((prev) => {
Array.from(prev.keys()).forEach((key) => {
if (filterIds.includes(key) && !(key in value)) {
if (prefixedFilterIds.includes(key) && !(key in value)) {
prev.delete(key)
}
})

Object.entries(value).forEach(([key, filter]) => {
if (filterIds.includes(key) && filter.value) {
if (
prefixedFilterIds.includes(getQueryParamKey(key, prefix)) &&
filter.value
) {
prev.set(getQueryParamKey(key, prefix), JSON.stringify(filter.value))
}
})
Expand Down
10 changes: 9 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/$schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,12 @@
},
"to": {
"type": "string"
},
"starting": {
"type": "string"
},
"ending": {
"type": "string"
}
},
"required": [
Expand All @@ -1252,7 +1258,9 @@
"lastTwelveMonths",
"custom",
"from",
"to"
"to",
"starting",
"ending"
],
"additionalProperties": false
},
Expand Down
4 changes: 3 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,9 @@
"lastTwelveMonths": "Last 12 months",
"custom": "Custom",
"from": "From",
"to": "To"
"to": "To",
"starting": "Starting",
"ending": "Ending"
},
"compare": {
"lessThan": "Less than",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ const useDateFilterOptions = () => {

const useFilters = () => {
const { t } = useTranslation()
const { getFullDate } = useDate()
const dateFilterOptions = useDateFilterOptions()

return useMemo(() => {
Expand All @@ -223,14 +224,22 @@ const useFilters = () => {
type: "date",
label: t("fields.createdAt"),
format: "date",
formatDateValue: (date) => getFullDate({ date }),
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
options: dateFilterOptions,
}),
filterHelper.accessor("updated_at", {
type: "date",
label: t("fields.updatedAt"),
format: "date",
rangeOptionStartLabel: t("filters.date.starting"),
rangeOptionEndLabel: t("filters.date.ending"),
rangeOptionLabel: t("filters.date.custom"),
formatDateValue: (date) => getFullDate({ date }),
options: dateFilterOptions,
}),
]
}, [t, dateFilterOptions])
}, [t, dateFilterOptions, getFullDate])
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DropdownMenu } from "@/components/dropdown-menu"
import { clx } from "@/utils/clx"

import { DatePicker } from "../../../components/date-picker"
import { Label } from "../../../components/label"
import { useDataTableContext } from "../context/use-data-table-context"
import { DateComparisonOperator, DateFilterProps, FilterOption } from "../types"
import { isDateComparisonOperator } from "../utils/is-date-comparison-operator"
Expand All @@ -16,9 +17,20 @@ interface DataTableFilterProps {
filter: ColumnFilter
}

const DEFAULT_FORMAT_DATE_VALUE = (d: Date) =>
d.toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
})
const DEFAULT_RANGE_OPTION_LABEL = "Custom"
const DEFAULT_RANGE_OPTION_START_LABEL = "Starting"
const DEFAULT_RANGE_OPTION_END_LABEL = "Ending"

const DataTableFilter = ({ filter }: DataTableFilterProps) => {
const { instance } = useDataTableContext()
const [open, setOpen] = React.useState(filter.value === undefined)
const [isCustom, setIsCustom] = React.useState(false)

const onOpenChange = React.useCallback(
(open: boolean) => {
Expand Down Expand Up @@ -62,12 +74,37 @@ const DataTableFilter = ({ filter }: DataTableFilterProps) => {
}

return (
!isCustom &&
(value.$gte === o.value.$gte || (!value.$gte && !o.value.$gte)) &&
(value.$lte === o.value.$lte || (!value.$lte && !o.value.$lte)) &&
(value.$gt === o.value.$gt || (!value.$gt && !o.value.$gt)) &&
(value.$lt === o.value.$lt || (!value.$lt && !o.value.$lt))
)
})?.label ?? null

if (!displayValue && isDateFilterProps(meta)) {
const formatDateValue = meta.formatDateValue
? meta.formatDateValue
: DEFAULT_FORMAT_DATE_VALUE

if (value.$gte && !value.$lte) {
displayValue = `${
meta.rangeOptionStartLabel || DEFAULT_RANGE_OPTION_START_LABEL
} ${formatDateValue(new Date(value.$gte))}`
}

if (value.$lte && !value.$gte) {
displayValue = `${
meta.rangeOptionEndLabel || DEFAULT_RANGE_OPTION_END_LABEL
} ${formatDateValue(new Date(value.$lte))}`
}

if (value.$gte && value.$lte) {
displayValue = `${formatDateValue(
new Date(value.$gte)
)} - ${formatDateValue(new Date(value.$lte))}`
}
}
}

return displayValue
Expand Down Expand Up @@ -137,6 +174,7 @@ const DataTableFilter = ({ filter }: DataTableFilterProps) => {
<DataTableFilterDateContent
filter={filter}
options={options as FilterOption<DateComparisonOperator>[]}
setIsCustom={setIsCustom}
{...rest}
/>
)
Expand All @@ -152,7 +190,15 @@ const DataTableFilter = ({ filter }: DataTableFilterProps) => {
type DataTableFilterDateContentProps = {
filter: ColumnFilter
options: FilterOption<DateComparisonOperator>[]
} & Pick<DateFilterProps, "format" | "rangeOptionLabel" | "disableRangeOption">
setIsCustom: (isCustom: boolean) => void
} & Pick<
DateFilterProps,
| "format"
| "rangeOptionLabel"
| "disableRangeOption"
| "rangeOptionStartLabel"
| "rangeOptionEndLabel"
>

function getIsCustomOptionSelected(
options: FilterOption<DateComparisonOperator>[],
Expand All @@ -171,15 +217,18 @@ function getIsCustomOptionSelected(
return false
}

return value.$gte || value.$lte
return !!value.$gte || !!value.$lte
}

const DataTableFilterDateContent = ({
filter,
options,
format = "date",
rangeOptionLabel = "Custom",
rangeOptionLabel = DEFAULT_RANGE_OPTION_LABEL,
rangeOptionStartLabel = DEFAULT_RANGE_OPTION_START_LABEL,
rangeOptionEndLabel = DEFAULT_RANGE_OPTION_END_LABEL,
disableRangeOption = false,
setIsCustom,
}: DataTableFilterDateContentProps) => {
const currentValue = filter.value as DateComparisonOperator | undefined
const { instance } = useDataTableContext()
Expand All @@ -188,13 +237,17 @@ const DataTableFilterDateContent = ({
getIsCustomOptionSelected(options, currentValue)
)

React.useEffect(() => {
setIsCustom(showCustom)
}, [showCustom])

const selectedValue = React.useMemo(() => {
if (!currentValue) {
if (!currentValue || showCustom) {
return undefined
}

return JSON.stringify(currentValue)
}, [currentValue])
}, [currentValue, showCustom])

const onValueChange = React.useCallback(
(valueStr: string) => {
Expand Down Expand Up @@ -268,19 +321,31 @@ const DataTableFilterDateContent = ({
{!disableRangeOption && showCustom && (
<React.Fragment>
<DropdownMenu.Separator />
<div className="flex flex-col gap-2">
<DatePicker
granularity={granularity}
value={currentValue?.$gte ? new Date(currentValue.$gte) : null}
onChange={(value) => onCustomValueChange("$gte", value)}
maxValue={maxDate}
/>
<DatePicker
granularity={granularity}
value={currentValue?.$lte ? new Date(currentValue.$lte) : null}
onChange={(value) => onCustomValueChange("$lte", value)}
minValue={minDate}
/>
<div className="flex flex-col gap-2 px-2 pb-3 pt-1">
<div className="flex flex-col gap-1">
<Label id="custom-start-date-label" size="xsmall" weight="plus">
{rangeOptionStartLabel}
</Label>
<DatePicker
aria-labelledby="custom-start-date-label"
granularity={granularity}
value={currentValue?.$gte ? new Date(currentValue.$gte) : null}
onChange={(value) => onCustomValueChange("$gte", value)}
maxValue={maxDate}
/>
</div>
<div className="flex flex-col gap-1">
<Label id="custom-end-date-label" size="xsmall" weight="plus">
{rangeOptionEndLabel}
</Label>
<DatePicker
aria-labelledby="custom-end-date-label"
granularity={granularity}
value={currentValue?.$lte ? new Date(currentValue.$lte) : null}
onChange={(value) => onCustomValueChange("$lte", value)}
minValue={minDate}
/>
</div>
</div>
</React.Fragment>
)}
Expand Down Expand Up @@ -375,5 +440,13 @@ const DataTableFilterRadioContent = ({
)
}

function isDateFilterProps(props?: unknown | null): props is DateFilterProps {
if (!props) {
return false
}

return (props as DateFilterProps).type === "date"
}

export { DataTableFilter }
export type { DataTableFilterProps }
3 changes: 3 additions & 0 deletions packages/design-system/ui/src/blocks/data-table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ export interface DateFilterProps extends BaseFilterProps {
*/
format?: "date" | "date-time"
rangeOptionLabel?: string
rangeOptionStartLabel?: string
rangeOptionEndLabel?: string
disableRangeOption?: boolean
formatDateValue?: (value: Date) => string
options: FilterOption<DateComparisonOperator>[]
}

Expand Down

0 comments on commit 283c15a

Please sign in to comment.