Skip to content

Commit

Permalink
Add new age filter in QB
Browse files Browse the repository at this point in the history
Fixes #5180
  • Loading branch information
CarolineDenis committed Sep 12, 2024
1 parent dfa30a6 commit d4841a8
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useTriggerState } from '../../hooks/useTriggerState';
import { useValidation } from '../../hooks/useValidation';
import { commonText } from '../../localization/common';
import { queryText } from '../../localization/query';
import { resourcesText } from '../../localization/resources';
import { f } from '../../utils/functools';
import type { Parser } from '../../utils/parser/definitions';
import {
Expand Down Expand Up @@ -37,6 +38,7 @@ import { SpecifyUserAutoComplete } from './SpecifyUserAutoComplete';
* See https://github.com/specify/specify7/issues/318
*/
export type QueryFieldType =
| 'age'
| 'aggregator'
| 'checkbox'
| 'date'
Expand All @@ -58,6 +60,8 @@ export type QueryFieldFilter =
| 'less'
| 'lessOrEqual'
| 'like'
| 'name'
| 'range'
| 'startsWith'
| 'true'
| 'trueOrNull';
Expand Down Expand Up @@ -560,6 +564,24 @@ export const queryFieldFilters: RR<
types: ['checkbox'],
hasParser: true,
},
name: {
id: 15,
label: resourcesText.name(),
description: undefined,
renderPickList: true,
types: ['age'],
hasParser: true,
component: In,
},
range: {
id: 16,
label: queryText.range(),
description: undefined,
renderPickList: false,
types: ['age'],
hasParser: true,
component: Between,
},
};

export function QueryLineFilter({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from 'react';

import { commonText } from '../../localization/common';
import { queryText } from '../../localization/query';
import type { RA } from '../../utils/types';
import { Button } from '../Atoms/Button';
import { className } from '../Atoms/className';
import { iconClassName, icons } from '../Atoms/Icons';
import {
mappingElementDivider,
mappingElementDividerClassName,
} from '../WbPlanView/LineComponents';
import type { QueryFieldFilter } from './FieldFilter';
import type { QueryField } from './helpers';
export function FieldFilterTool({
fieldFilters,
index,
isBasic,
hasAny,
isFieldComplete,
handleChange,
handleFilterChange,
}: {
readonly fieldFilters: RA<{
readonly type: QueryFieldFilter;
readonly startValue: string;
readonly isNot: boolean;
}>;
readonly index: number;
readonly isBasic: boolean;
readonly hasAny: boolean;
readonly isFieldComplete: boolean;
readonly handleChange: ((newField: QueryField) => void) | undefined;
readonly handleFilterChange: (
index: number,
filter: QueryField['filters'][number] | undefined
) => void;
}): JSX.Element {
return (
<>
{index === 0 ? (
<>
{isBasic ? null : mappingElementDivider}
{!hasAny && (
<Button.Small
aria-label={queryText.or()}
aria-pressed={fieldFilters.length > 1}
className={`
print:hidden
${className.ariaHandled}
${isFieldComplete ? '' : 'invisible'}
`}
disabled={handleChange === undefined}
title={queryText.or()}
variant={
fieldFilters.length > 1
? className.infoButton
: className.secondaryLightButton
}
onClick={(): void =>
handleFilterChange(fieldFilters.length, {
type: 'any',
isNot: false,
startValue: '',
})
}
>
{icons.plus}
</Button.Small>
)}
</>
) : (
<>
<span className={mappingElementDividerClassName}>
<span
className={`
flex items-center justify-center uppercase
${iconClassName}
`}
>
{queryText.or()}
</span>
</span>
<Button.Small
aria-label={commonText.remove()}
className="print:hidden"
disabled={handleChange === undefined}
title={commonText.remove()}
variant={className.dangerButton}
onClick={(): void => handleFilterChange(index, undefined)}
>
{icons.trash}
</Button.Small>
</>
)}
{fieldFilters[index].type !== 'any' && (
<Button.Small
aria-label={queryText.negate()}
aria-pressed={fieldFilters[index].isNot}
className={className.ariaHandled}
disabled={handleChange === undefined}
title={queryText.negate()}
variant={
fieldFilters[index].isNot
? className.dangerButton
: className.secondaryLightButton
}
onClick={(): void =>
handleFilterChange(index, {
...fieldFilters[index],
isNot: !fieldFilters[index].isNot,
})
}
>
{icons.ban}
</Button.Small>
)}
</>
);
}
89 changes: 10 additions & 79 deletions specifyweb/frontend/js_src/lib/components/QueryBuilder/Line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
queryFieldFilters,
QueryLineFilter,
} from './FieldFilter';
import { FieldFilterTool } from './FieldFilterTool';
import type { DatePart } from './fieldSpec';
import { QueryFieldSpec } from './fieldSpec';
import { QueryFieldFormatter } from './Formatter';
Expand Down Expand Up @@ -409,85 +410,15 @@ export function QueryLine({
key={index}
>
<div className="flex contents items-center gap-2">
{index === 0 ? (
<>
{isBasic ? null : mappingElementDivider}
{!hasAny && (
<Button.Small
aria-label={queryText.or()}
aria-pressed={field.filters.length > 1}
className={`
print:hidden
${className.ariaHandled}
${isFieldComplete ? '' : 'invisible'}
`}
disabled={handleChange === undefined}
title={queryText.or()}
variant={
field.filters.length > 1
? className.infoButton
: className.secondaryLightButton
}
onClick={(): void =>
handleFilterChange(field.filters.length, {
type: 'any',
isNot: false,
startValue: '',
})
}
>
{icons.plus}
</Button.Small>
)}
</>
) : (
<>
<span className={mappingElementDividerClassName}>
<span
className={`
flex items-center justify-center uppercase
${iconClassName}
`}
>
{queryText.or()}
</span>
</span>
<Button.Small
aria-label={commonText.remove()}
className="print:hidden"
disabled={handleChange === undefined}
title={commonText.remove()}
variant={className.dangerButton}
onClick={(): void =>
handleFilterChange(index, undefined)
}
>
{icons.trash}
</Button.Small>
</>
)}
{field.filters[index].type !== 'any' && (
<Button.Small
aria-label={queryText.negate()}
aria-pressed={field.filters[index].isNot}
className={className.ariaHandled}
disabled={handleChange === undefined}
title={queryText.negate()}
variant={
field.filters[index].isNot
? className.dangerButton
: className.secondaryLightButton
}
onClick={(): void =>
handleFilterChange(index, {
...field.filters[index],
isNot: !field.filters[index].isNot,
})
}
>
{icons.ban}
</Button.Small>
)}
<FieldFilterTool
fieldFilters={field.filters}
handleChange={handleChange}
handleFilterChange={handleFilterChange}
hasAny={hasAny}
index={index}
isBasic={isBasic}
isFieldComplete={isFieldComplete}
/>
<div className="contents w-full">
<Select
aria-label={
Expand Down
3 changes: 3 additions & 0 deletions specifyweb/frontend/js_src/lib/localization/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,4 +910,7 @@ export const queryText = createDictionary({
'ru-ru': 'Выберите форматтер',
'uk-ua': 'Виберіть форматер',
},
range: {
'en-us': 'Range',
},
} as const);

0 comments on commit d4841a8

Please sign in to comment.