1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-04 05:05:24 +00:00

Resolve "Higher than and lower than frontend filters are not working with formulas"

This commit is contained in:
Davide Silvestri 2024-01-24 09:46:42 +00:00
parent 59192c94f3
commit 709e895494
9 changed files with 174 additions and 55 deletions

View file

@ -0,0 +1,7 @@
{
"type": "bug",
"message": "Fix higher_than and lower_than frontend view filters for formula fields.",
"issue_number": 2289,
"bullet_points": [],
"created_at": "2024-01-17"
}

View file

@ -144,8 +144,6 @@ export default {
this.$registry this.$registry
) )
console.log(newRowValues)
this.$store.dispatch('rowModal/updated', { this.$store.dispatch('rowModal/updated', {
tableId: this.tableId, tableId: this.tableId,
values: newRowValues, values: newRowValues,

View file

@ -36,7 +36,11 @@ export default {
methods: { methods: {
formattedDuration(value) { formattedDuration(value) {
const metadata = this.entry.fields_metadata[this.fieldIdentifier] const metadata = this.entry.fields_metadata[this.fieldIdentifier]
return DurationFieldType.formatValue(metadata, value) const durationFieldType = this.$registry.get(
'field',
DurationFieldType.getType()
)
return durationFieldType.formatValue(metadata, value)
}, },
}, },
} }

View file

@ -7,14 +7,14 @@
</template> </template>
<script> <script>
import { DurationFieldType } from '@baserow/modules/database/fieldTypes' import { formatDurationValue } from '@baserow/modules/database/utils/duration'
export default { export default {
name: 'FunctionalGridViewFieldDuration', name: 'FunctionalGridViewFieldDuration',
functional: true, functional: true,
methods: { methods: {
formatValue(field, value) { formatValue(field, value) {
return DurationFieldType.formatValue(field, value) return formatDurationValue(value, field.duration_format)
}, },
}, },
} }

View file

@ -741,7 +741,7 @@ export class FieldType extends Registerable {
* moment object, or a duration field can accept a string like '1:30' to * moment object, or a duration field can accept a string like '1:30' to
* convert it to a number of seconds. * convert it to a number of seconds.
*/ */
static parseInputValue(field, value) { parseInputValue(field, value) {
return value return value
} }
} }
@ -1351,7 +1351,7 @@ export class NumberFieldType extends FieldType {
return true return true
} }
static parseInputValue(field, value) { parseInputValue(field, value) {
return parseFloat(value) return parseFloat(value)
} }
} }
@ -1683,8 +1683,8 @@ class BaseDateFieldType extends FieldType {
* correct format for the field. If it can't be parsed null is returned. * correct format for the field. If it can't be parsed null is returned.
*/ */
prepareValueForPaste(field, clipboardData, richClipboardData) { prepareValueForPaste(field, clipboardData, richClipboardData) {
const dateValue = DateFieldType.parseInputValue(field, clipboardData || '') const dateValue = this.parseInputValue(field, clipboardData || '')
return DateFieldType.formatDate(field, dateValue) return this.formatValue(field, dateValue)
} }
/** /**
@ -1724,7 +1724,7 @@ class BaseDateFieldType extends FieldType {
return formats return formats
} }
static parseInputValue(field, dateString) { parseInputValue(field, dateString) {
const formats = DateFieldType.getDateFormatsOptionsForValue( const formats = DateFieldType.getDateFormatsOptionsForValue(
field, field,
dateString dateString
@ -1751,8 +1751,8 @@ class BaseDateFieldType extends FieldType {
return date return date
} }
static formatDate(field, date) { formatValue(field, value) {
const momentDate = moment.utc(date) const momentDate = moment.utc(value)
if (momentDate.isValid()) { if (momentDate.isValid()) {
return field.date_include_time return field.date_include_time
? momentDate.format() ? momentDate.format()
@ -1838,9 +1838,9 @@ export class DateFieldType extends BaseDateFieldType {
} }
parseQueryParameter(field, value) { parseQueryParameter(field, value) {
return DateFieldType.formatDate( return this.formatValue(
field.field, field.field,
DateFieldType.parseInputValue(field.field, value) this.parseInputValue(field.field, value)
) )
} }
} }
@ -2280,11 +2280,11 @@ export class DurationFieldType extends FieldType {
} }
parseQueryParameter(field, value, options) { parseQueryParameter(field, value, options) {
return DurationFieldType.parseInputValue(field, value) return this.parseInputValue(field.field, value)
} }
toSearchableString(field, value, delimiter = ', ') { toSearchableString(field, value, delimiter = ', ') {
return DurationFieldType.formatValue(field, value) return this.formatValue(field, value)
} }
getSort(name, order) { getSort(name, order) {
@ -2350,22 +2350,22 @@ export class DurationFieldType extends FieldType {
return RowHistoryFieldDuration return RowHistoryFieldDuration
} }
static formatValue(field, value) { formatValue(field, value) {
return formatDurationValue(value, field.duration_format) return formatDurationValue(value, field.duration_format)
} }
static parseInputValue(field, value) { parseInputValue(field, value) {
const format = field.duration_format const format = field.duration_format
const preparedValue = parseDurationValue(value, format) const preparedValue = parseDurationValue(value, format)
return roundDurationValueToFormat(preparedValue, format) return roundDurationValueToFormat(preparedValue, format)
} }
toHumanReadableString(field, value, delimiter = ', ') { toHumanReadableString(field, value, delimiter = ', ') {
return DurationFieldType.formatValue(field, value) return this.formatValue(field, value)
} }
prepareValueForCopy(field, value) { prepareValueForCopy(field, value) {
return DurationFieldType.formatValue(field, value) return this.formatValue(field, value)
} }
prepareRichValueForCopy(field, value) { prepareRichValueForCopy(field, value) {
@ -2376,7 +2376,7 @@ export class DurationFieldType extends FieldType {
if (richClipboardData && isNumeric(richClipboardData)) { if (richClipboardData && isNumeric(richClipboardData)) {
return richClipboardData return richClipboardData
} }
return DurationFieldType.parseInputValue(field, clipboardData) return this.parseInputValue(field, clipboardData)
} }
getCanGroupByInView(field) { getCanGroupByInView(field) {
@ -3543,6 +3543,14 @@ export class FormulaFieldType extends FieldType {
const subType = this.app.$registry.get('formula_type', field.formula_type) const subType = this.app.$registry.get('formula_type', field.formula_type)
return subType.canGroupByInView(field) return subType.canGroupByInView(field)
} }
parseInputValue(field, value) {
const underlyingFieldType = this.app.$registry.get(
'field',
this._mapFormulaTypeToFieldType(field.formula_type)
)
return underlyingFieldType.parseInputValue(field, value)
}
} }
export class CountFieldType extends FormulaFieldType { export class CountFieldType extends FormulaFieldType {

View file

@ -12,6 +12,11 @@ export default {
formattedValue: '', formattedValue: '',
} }
}, },
computed: {
fieldType() {
return this.$registry.get('field', DurationFieldType.getType())
},
},
watch: { watch: {
value(value) { value(value) {
this.updateFormattedValue(this.field, value) this.updateFormattedValue(this.field, value)
@ -31,7 +36,7 @@ export default {
return this.errorMsg return this.errorMsg
}, },
formatValue(field, value) { formatValue(field, value) {
return DurationFieldType.formatValue(field, value) return this.fieldType.formatValue(field, value)
}, },
updateFormattedValue(field, value) { updateFormattedValue(field, value) {
this.formattedValue = this.formatValue(field, value) this.formattedValue = this.formatValue(field, value)
@ -41,7 +46,7 @@ export default {
if (this.errorMsg !== null) { if (this.errorMsg !== null) {
return return
} }
const newCopy = DurationFieldType.parseInputValue(field, value) const newCopy = this.fieldType.parseInputValue(field, value)
if (newCopy !== this.copy) { if (newCopy !== this.copy) {
this.copy = newCopy this.copy = newCopy
return newCopy return newCopy

View file

@ -1341,13 +1341,9 @@ export class HigherThanViewFilterType extends ViewFilterType {
return true return true
} }
rowValue = fieldType.constructor.parseInputValue(field, rowValue) const rowVal = fieldType.parseInputValue(field, rowValue)
filterValue = fieldType.constructor.parseInputValue(field, filterValue) const fltVal = fieldType.parseInputValue(field, filterValue)
return ( return Number.isFinite(rowVal) && Number.isFinite(fltVal) && rowVal > fltVal
Number.isFinite(rowValue) &&
Number.isFinite(filterValue) &&
rowValue > filterValue
)
} }
} }
@ -1388,13 +1384,9 @@ export class LowerThanViewFilterType extends ViewFilterType {
return true return true
} }
rowValue = fieldType.constructor.parseInputValue(field, rowValue) const rowVal = fieldType.parseInputValue(field, rowValue)
filterValue = fieldType.constructor.parseInputValue(field, filterValue) const fltVal = fieldType.parseInputValue(field, filterValue)
return ( return Number.isFinite(rowVal) && Number.isFinite(fltVal) && rowVal < fltVal
Number.isFinite(rowValue) &&
Number.isFinite(filterValue) &&
rowValue < filterValue
)
} }
} }

View file

@ -603,17 +603,20 @@ const queryParametersForParsing = [
}, },
{ {
fieldType: new DurationFieldType(), fieldType: new DurationFieldType(),
input: { value: '1:01', field: { duration_format: 'h:mm' } }, input: { value: '1:01', field: { field: { duration_format: 'h:mm' } } },
output: 3660, output: 3660,
}, },
{ {
fieldType: new DurationFieldType(), fieldType: new DurationFieldType(),
input: { value: '1:01:01', field: { duration_format: 'h:mm:ss' } }, input: {
value: '1:01:01',
field: { field: { duration_format: 'h:mm:ss' } },
},
output: 3661, output: 3661,
}, },
{ {
fieldType: new DurationFieldType(), fieldType: new DurationFieldType(),
input: { value: 61, field: { duration_format: 'h:mm' } }, input: { value: 61, field: { field: { duration_format: 'h:mm' } } },
output: 60, // the value is rounded according to the duration format output: 60, // the value is rounded according to the duration format
}, },
] ]

View file

@ -31,7 +31,11 @@ import {
HigherThanViewFilterType, HigherThanViewFilterType,
LowerThanViewFilterType, LowerThanViewFilterType,
} from '@baserow/modules/database/viewFilters' } from '@baserow/modules/database/viewFilters'
import { DurationFieldType } from '@baserow/modules/database/fieldTypes' import {
DurationFieldType,
NumberFieldType,
FormulaFieldType,
} from '@baserow/modules/database/fieldTypes'
const dateBeforeCases = [ const dateBeforeCases = [
{ {
@ -1126,6 +1130,42 @@ const durationLowerThanCases = [
}, },
] ]
const numberValueIsHigherThanCases = [
{
rowValue: 2,
filterValue: 0,
expected: true,
},
{
rowValue: null,
filterValue: 0,
expected: false,
},
{
rowValue: 0,
filterValue: '0',
expected: false,
},
]
const numberValueIsLowerThanCases = [
{
rowValue: 1,
filterValue: '2',
expected: true,
},
{
rowValue: null,
filterValue: 0,
expected: false,
},
{
rowValue: 0,
filterValue: '0',
expected: false,
},
]
describe('All Tests', () => { describe('All Tests', () => {
let testApp = null let testApp = null
@ -1405,10 +1445,28 @@ describe('All Tests', () => {
).toBe(true) ).toBe(true)
}) })
test.each(durationHigherThanCases)('HigherThanFilterType', (values) => { test.each(durationHigherThanCases)(
const fieldType = new DurationFieldType() 'DurationHigherThanFilterType',
(values) => {
const fieldType = new DurationFieldType({
app: testApp,
})
const { field } = values.context
const result = new HigherThanViewFilterType({ app: testApp }).matches(
values.rowValue,
values.filterValue,
field,
fieldType
)
expect(result).toBe(values.expected)
}
)
test.each(durationLowerThanCases)('DurationLowerThanFilterType', (values) => {
const app = testApp.getApp()
const fieldType = new DurationFieldType({ app })
const { field } = values.context const { field } = values.context
const result = new HigherThanViewFilterType({ app: testApp }).matches( const result = new LowerThanViewFilterType({ app }).matches(
values.rowValue, values.rowValue,
values.filterValue, values.filterValue,
field, field,
@ -1417,15 +1475,59 @@ describe('All Tests', () => {
expect(result).toBe(values.expected) expect(result).toBe(values.expected)
}) })
test.each(durationLowerThanCases)('LowerThanFilterType', (values) => { test.each(numberValueIsHigherThanCases)(
const fieldType = new DurationFieldType() 'NumberHigherThanFilterType',
const { field } = values.context (values) => {
const result = new LowerThanViewFilterType({ app: testApp }).matches( const app = testApp.getApp()
values.rowValue, const result = new HigherThanViewFilterType({ app }).matches(
values.filterValue, values.rowValue,
field, values.filterValue,
fieldType { type: 'number' },
) new NumberFieldType({ app })
expect(result).toBe(values.expected) )
}) expect(result).toBe(values.expected)
}
)
test.each(numberValueIsHigherThanCases)(
'FormulaNumberHigherThanFilterType',
(values) => {
const app = testApp.getApp()
const result = new HigherThanViewFilterType({ app }).matches(
values.rowValue,
values.filterValue,
{ type: 'formula', formula_type: 'number' },
new FormulaFieldType({ app })
)
expect(result).toBe(values.expected)
}
)
test.each(numberValueIsLowerThanCases)(
'NumberLowerThanFilterType',
(values) => {
const app = testApp.getApp()
const result = new LowerThanViewFilterType({ app }).matches(
values.rowValue,
values.filterValue,
{ type: 'number' },
new NumberFieldType({ app })
)
expect(result).toBe(values.expected)
}
)
test.each(numberValueIsLowerThanCases)(
'FormulaNumberLowerThanFilterType',
(values) => {
const app = testApp.getApp()
const result = new LowerThanViewFilterType({ app }).matches(
values.rowValue,
values.filterValue,
{ type: 'formula', formula_type: 'number' },
new FormulaFieldType({ app })
)
expect(result).toBe(values.expected)
}
)
}) })