fix(filter): do not replace labels keyword when the value is 'label'
Resolves https://community.vikunja.io/t/filtering-by-label-ux-issues/2393/16 (cherry picked from commit f4a7326b684cbfbad20a496ddc1c79c59c0672f0)
This commit is contained in:
parent
971f328256
commit
a266fbf2b9
@ -147,6 +147,26 @@ describe('Filter Transformation', () => {
|
|||||||
|
|
||||||
expect(transformed).toBe('start_date > now')
|
expect(transformed).toBe('start_date > now')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should correctly resolve label when the label is called label', () => {
|
||||||
|
const transformed = transformFilterStringForApi(
|
||||||
|
'labels = label',
|
||||||
|
(title: string) => 1,
|
||||||
|
nullTitleToIdResolver,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(transformed).toBe('labels = 1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should correctly resolve project when the project is called project', () => {
|
||||||
|
const transformed = transformFilterStringForApi(
|
||||||
|
'project = project',
|
||||||
|
nullTitleToIdResolver,
|
||||||
|
(title: string) => 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(transformed).toBe('project = 1')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('To API', () => {
|
describe('To API', () => {
|
||||||
|
@ -77,38 +77,23 @@ export function transformFilterStringForApi(
|
|||||||
filter = filter.replace(new RegExp(f, 'ig'), f)
|
filter = filter.replace(new RegExp(f, 'ig'), f)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Transform labels to ids
|
// Transform labels and projects to ids
|
||||||
LABEL_FIELDS.forEach(field => {
|
function transformFieldToIds(
|
||||||
|
fields: string[],
|
||||||
|
resolver: (title: string) => number | null,
|
||||||
|
filter: string
|
||||||
|
): string {
|
||||||
|
fields.forEach(field => {
|
||||||
const pattern = getFilterFieldRegexPattern(field)
|
const pattern = getFilterFieldRegexPattern(field)
|
||||||
|
|
||||||
let match: RegExpExecArray | null
|
let match: RegExpExecArray | null
|
||||||
while ((match = pattern.exec(filter)) !== null) {
|
while ((match = pattern.exec(filter)) !== null) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [matched, prefix, operator, space, keyword] = match
|
const [matched, prefix, operator, space, keyword] = match
|
||||||
if (keyword) {
|
if (!keyword) {
|
||||||
let keywords = [keyword.trim()]
|
continue
|
||||||
if (operator === 'in' || operator === '?=') {
|
|
||||||
keywords = keyword.trim().split(',').map(k => k.trim())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keywords.forEach(k => {
|
|
||||||
const labelId = labelResolver(k)
|
|
||||||
if (labelId !== null) {
|
|
||||||
filter = filter.replace(k, String(labelId))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Transform projects to ids
|
|
||||||
PROJECT_FIELDS.forEach(field => {
|
|
||||||
const pattern = getFilterFieldRegexPattern(field)
|
|
||||||
|
|
||||||
let match: RegExpExecArray | null
|
|
||||||
while ((match = pattern.exec(filter)) !== null) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [matched, prefix, operator, space, keyword] = match
|
|
||||||
if (keyword) {
|
|
||||||
let keywords = [keyword.trim()]
|
let keywords = [keyword.trim()]
|
||||||
if (operator === 'in' || operator === '?=') {
|
if (operator === 'in' || operator === '?=') {
|
||||||
keywords = keyword.trim().split(',').map(k => k.trim())
|
keywords = keyword.trim().split(',').map(k => k.trim())
|
||||||
@ -117,9 +102,9 @@ export function transformFilterStringForApi(
|
|||||||
let replaced = keyword
|
let replaced = keyword
|
||||||
|
|
||||||
keywords.forEach(k => {
|
keywords.forEach(k => {
|
||||||
const projectId = projectResolver(k)
|
const id = resolver(k)
|
||||||
if (projectId !== null) {
|
if (id !== null) {
|
||||||
replaced = replaced.replace(k, String(projectId))
|
replaced = replaced.replace(k, String(id))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -128,8 +113,15 @@ export function transformFilterStringForApi(
|
|||||||
replaced +
|
replaced +
|
||||||
filter.substring(actualKeywordStart + keyword.length)
|
filter.substring(actualKeywordStart + keyword.length)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform labels to ids
|
||||||
|
filter = transformFieldToIds(LABEL_FIELDS, labelResolver, filter)
|
||||||
|
|
||||||
|
// Transform projects to ids
|
||||||
|
filter = transformFieldToIds(PROJECT_FIELDS, projectResolver, filter)
|
||||||
|
|
||||||
// Transform all attributes to snake case
|
// Transform all attributes to snake case
|
||||||
AVAILABLE_FILTER_FIELDS.forEach(f => {
|
AVAILABLE_FILTER_FIELDS.forEach(f => {
|
||||||
@ -154,53 +146,40 @@ export function transformFilterStringFromApi(
|
|||||||
filter = filter.replaceAll(snakeCase(f), f)
|
filter = filter.replaceAll(snakeCase(f), f)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Function to transform fields to their titles
|
||||||
|
function transformFieldsToTitles(
|
||||||
|
fields: string[],
|
||||||
|
resolver: (id: number) => string | null | undefined
|
||||||
|
) {
|
||||||
|
fields.forEach(field => {
|
||||||
|
const pattern = getFilterFieldRegexPattern(field)
|
||||||
|
|
||||||
|
let match: RegExpExecArray | null
|
||||||
|
while ((match = pattern.exec(filter)) !== null) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [matched, prefix, operator, space, keyword] = match
|
||||||
|
if (keyword) {
|
||||||
|
let keywords = [keyword.trim()]
|
||||||
|
if (operator === 'in' || operator === '?=') {
|
||||||
|
keywords = keyword.trim().split(',').map(k => k.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
keywords.forEach(k => {
|
||||||
|
const title = resolver(parseInt(k))
|
||||||
|
if (title) {
|
||||||
|
filter = filter.replace(k, title)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Transform labels to their titles
|
// Transform labels to their titles
|
||||||
LABEL_FIELDS.forEach(field => {
|
transformFieldsToTitles(LABEL_FIELDS, labelResolver)
|
||||||
const pattern = getFilterFieldRegexPattern(field)
|
|
||||||
|
|
||||||
let match: RegExpExecArray | null
|
// Transform projects to their titles
|
||||||
while ((match = pattern.exec(filter)) !== null) {
|
transformFieldsToTitles(PROJECT_FIELDS, projectResolver)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [matched, prefix, operator, space, keyword] = match
|
|
||||||
if (keyword) {
|
|
||||||
let keywords = [keyword.trim()]
|
|
||||||
if (operator === 'in' || operator === '?=') {
|
|
||||||
keywords = keyword.trim().split(',').map(k => k.trim())
|
|
||||||
}
|
|
||||||
|
|
||||||
keywords.forEach(k => {
|
|
||||||
const labelTitle = labelResolver(parseInt(k))
|
|
||||||
if (labelTitle) {
|
|
||||||
filter = filter.replace(k, labelTitle)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Transform projects to ids
|
|
||||||
PROJECT_FIELDS.forEach(field => {
|
|
||||||
const pattern = getFilterFieldRegexPattern(field)
|
|
||||||
|
|
||||||
let match: RegExpExecArray | null
|
|
||||||
while ((match = pattern.exec(filter)) !== null) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [matched, prefix, operator, space, keyword] = match
|
|
||||||
if (keyword) {
|
|
||||||
let keywords = [keyword.trim()]
|
|
||||||
if (operator === 'in' || operator === '?=') {
|
|
||||||
keywords = keyword.trim().split(',').map(k => k.trim())
|
|
||||||
}
|
|
||||||
|
|
||||||
keywords.forEach(k => {
|
|
||||||
const project = projectResolver(parseInt(k))
|
|
||||||
if (project) {
|
|
||||||
filter = filter.replace(k, project)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user