mirror of
https://github.com/strapi/strapi.git
synced 2025-09-18 13:02:18 +00:00
Merge pull request #12972 from strapi/fix/cm-list-number
CM: Fix displaying number fields in the DynamicTable
This commit is contained in:
commit
f05ce9b317
@ -1,11 +1,13 @@
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
|
||||
import isSingleRelation from './isSingleRelation';
|
||||
import isFieldTypeNumber from '../../../../utils/isFieldTypeNumber';
|
||||
|
||||
export default function hasContent(type, content, metadatas, fieldSchema) {
|
||||
if (type === 'component') {
|
||||
const {
|
||||
mainField: { name: mainFieldName },
|
||||
mainField: { name: mainFieldName, type: mainFieldType },
|
||||
} = metadatas;
|
||||
|
||||
// Repeatable fields show the ID as fallback, in case the mainField
|
||||
@ -14,7 +16,23 @@ export default function hasContent(type, content, metadatas, fieldSchema) {
|
||||
return content.length > 0;
|
||||
}
|
||||
|
||||
return !isEmpty(content[mainFieldName]);
|
||||
const value = content[mainFieldName];
|
||||
|
||||
/* The ID field reports itself as type `integer`, which makes it
|
||||
impossible to distinguish it from other number fields.
|
||||
|
||||
Biginteger fields need to be treated as strings, as `isNumber`
|
||||
doesn't deal with them.
|
||||
*/
|
||||
if (
|
||||
isFieldTypeNumber(mainFieldType) &&
|
||||
mainFieldType !== 'biginteger' &&
|
||||
mainFieldName !== 'id'
|
||||
) {
|
||||
return isNumber(value);
|
||||
}
|
||||
|
||||
return !isEmpty(value);
|
||||
}
|
||||
|
||||
if (type === 'relation') {
|
||||
@ -25,5 +43,13 @@ export default function hasContent(type, content, metadatas, fieldSchema) {
|
||||
return content.count > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Biginteger fields need to be treated as strings, as `isNumber`
|
||||
doesn't deal with them.
|
||||
*/
|
||||
if (isFieldTypeNumber(type) && type !== 'biginteger') {
|
||||
return isNumber(content);
|
||||
}
|
||||
|
||||
return !isEmpty(content);
|
||||
}
|
||||
|
@ -1,6 +1,39 @@
|
||||
import hasContent from '../hasContent';
|
||||
|
||||
describe('hasContent', () => {
|
||||
describe('number fields', () => {
|
||||
it('returns true for integer', () => {
|
||||
const normalizedContent = hasContent('integer', 1);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns false for string integer', () => {
|
||||
const normalizedContent = hasContent('integer', '1');
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false for undefined text', () => {
|
||||
const normalizedContent = hasContent('integer', undefined);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns true for float', () => {
|
||||
const normalizedContent = hasContent('float', 1.111);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true for decimal', () => {
|
||||
const normalizedContent = hasContent('decimal', 1.111);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true for biginteger', () => {
|
||||
const normalizedContent = hasContent('biginteger', '12345678901234567890');
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text', () => {
|
||||
it('returns true for text content', () => {
|
||||
const normalizedContent = hasContent('text', 'content');
|
||||
expect(normalizedContent).toEqual(true);
|
||||
@ -15,7 +48,9 @@ describe('hasContent', () => {
|
||||
const normalizedContent = hasContent('text', undefined);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('single component', () => {
|
||||
it('extracts content from single components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
@ -34,6 +69,80 @@ describe('hasContent', () => {
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('extracts integers from single components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: 1, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'integer' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('extracts integers from single components without content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: null, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'integer' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('extracts float from single components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: 1.11, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'float' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('extracts float from single components without content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: null, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'float' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('extracts decimal from single components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: 1.11, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'decimal' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('extracts decimal from single components without content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: null, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'decimal' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
|
||||
it('extracts biginteger from single components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: '12345678901234567890', id: 1 },
|
||||
{ mainField: { name: 'number', type: 'biginteger' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(true);
|
||||
});
|
||||
|
||||
it('extracts biginteger from single components without content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
{ number: null, id: 1 },
|
||||
{ mainField: { name: 'number', type: 'biginteger' } }
|
||||
);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('repeatable components', () => {
|
||||
it('extracts content from repeatable components with content', () => {
|
||||
const normalizedContent = hasContent(
|
||||
'component',
|
||||
@ -73,7 +182,9 @@ describe('hasContent', () => {
|
||||
);
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('relations', () => {
|
||||
it('extracts content from multiple relations with content', () => {
|
||||
const normalizedContent = hasContent('relation', { count: 1 }, undefined, {
|
||||
relation: 'manyToMany',
|
||||
@ -122,4 +233,5 @@ describe('hasContent', () => {
|
||||
});
|
||||
expect(normalizedContent).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -7,12 +7,14 @@ import toNumber from 'lodash/toNumber';
|
||||
import * as yup from 'yup';
|
||||
import { translatedErrors as errorsTrads } from '@strapi/helper-plugin';
|
||||
|
||||
import isFieldTypeNumber from '../../../utils/isFieldTypeNumber';
|
||||
|
||||
yup.addMethod(yup.mixed, 'defined', function() {
|
||||
return this.test('defined', errorsTrads.required, value => value !== undefined);
|
||||
return this.test('defined', errorsTrads.required, (value) => value !== undefined);
|
||||
});
|
||||
|
||||
yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
||||
return this.test('notEmptyMin', errorsTrads.min, value => {
|
||||
return this.test('notEmptyMin', errorsTrads.min, (value) => {
|
||||
if (isEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
@ -49,7 +51,7 @@ yup.addMethod(yup.string, 'isSuperior', function(message, min) {
|
||||
});
|
||||
});
|
||||
|
||||
const getAttributes = data => get(data, ['attributes'], {});
|
||||
const getAttributes = (data) => get(data, ['attributes'], {});
|
||||
|
||||
const createYupSchema = (
|
||||
model,
|
||||
@ -95,7 +97,7 @@ const createYupSchema = (
|
||||
if (attribute.repeatable === true) {
|
||||
const { min, max, required } = attribute;
|
||||
|
||||
let componentSchema = yup.lazy(value => {
|
||||
let componentSchema = yup.lazy((value) => {
|
||||
let baseSchema = yup.array().of(componentFieldSchema);
|
||||
|
||||
if (min) {
|
||||
@ -121,7 +123,7 @@ const createYupSchema = (
|
||||
|
||||
return acc;
|
||||
}
|
||||
const componentSchema = yup.lazy(obj => {
|
||||
const componentSchema = yup.lazy((obj) => {
|
||||
if (obj !== undefined) {
|
||||
return attribute.required === true && !options.isDraft
|
||||
? componentFieldSchema.defined()
|
||||
@ -152,7 +154,7 @@ const createYupSchema = (
|
||||
if (min) {
|
||||
if (attribute.required) {
|
||||
dynamicZoneSchema = dynamicZoneSchema
|
||||
.test('min', errorsTrads.min, value => {
|
||||
.test('min', errorsTrads.min, (value) => {
|
||||
if (options.isCreatingEntry) {
|
||||
return value && value.length >= min;
|
||||
}
|
||||
@ -163,7 +165,7 @@ const createYupSchema = (
|
||||
|
||||
return value !== null && value.length >= min;
|
||||
})
|
||||
.test('required', errorsTrads.required, value => {
|
||||
.test('required', errorsTrads.required, (value) => {
|
||||
if (options.isCreatingEntry) {
|
||||
return value !== null || value !== undefined;
|
||||
}
|
||||
@ -178,7 +180,7 @@ const createYupSchema = (
|
||||
dynamicZoneSchema = dynamicZoneSchema.notEmptyMin(min);
|
||||
}
|
||||
} else if (attribute.required && !options.isDraft) {
|
||||
dynamicZoneSchema = dynamicZoneSchema.test('required', errorsTrads.required, value => {
|
||||
dynamicZoneSchema = dynamicZoneSchema.test('required', errorsTrads.required, (value) => {
|
||||
if (options.isCreatingEntry) {
|
||||
return value !== null || value !== undefined;
|
||||
}
|
||||
@ -213,7 +215,7 @@ const createYupSchemaAttribute = (type, validations, options) => {
|
||||
if (type === 'json') {
|
||||
schema = yup
|
||||
.mixed(errorsTrads.json)
|
||||
.test('isJSON', errorsTrads.json, value => {
|
||||
.test('isJSON', errorsTrads.json, (value) => {
|
||||
if (value === undefined) {
|
||||
return true;
|
||||
}
|
||||
@ -236,19 +238,19 @@ const createYupSchemaAttribute = (type, validations, options) => {
|
||||
if (['number', 'integer', 'float', 'decimal'].includes(type)) {
|
||||
schema = yup
|
||||
.number()
|
||||
.transform(cv => (isNaN(cv) ? undefined : cv))
|
||||
.transform((cv) => (isNaN(cv) ? undefined : cv))
|
||||
.typeError();
|
||||
}
|
||||
|
||||
if (['date', 'datetime'].includes(type)) {
|
||||
schema = yup.date();
|
||||
}
|
||||
|
||||
if (type === 'biginteger') {
|
||||
schema = yup.string().matches(/^-?\d*$/);
|
||||
}
|
||||
|
||||
Object.keys(validations).forEach(validation => {
|
||||
if (['date', 'datetime'].includes(type)) {
|
||||
schema = yup.date();
|
||||
}
|
||||
|
||||
Object.keys(validations).forEach((validation) => {
|
||||
const validationValue = validations[validation];
|
||||
|
||||
if (
|
||||
@ -267,13 +269,13 @@ const createYupSchemaAttribute = (type, validations, options) => {
|
||||
if (options.isCreatingEntry) {
|
||||
schema = schema.required(errorsTrads.required);
|
||||
} else {
|
||||
schema = schema.test('required', errorsTrads.required, value => {
|
||||
schema = schema.test('required', errorsTrads.required, (value) => {
|
||||
// Field is not touched and the user is editing the entry
|
||||
if (value === undefined && !options.isFromComponent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (['number', 'integer', 'biginteger', 'float', 'decimal'].includes(type)) {
|
||||
if (isFieldTypeNumber(type)) {
|
||||
if (value === 0) {
|
||||
return true;
|
||||
}
|
||||
@ -344,12 +346,12 @@ const createYupSchemaAttribute = (type, validations, options) => {
|
||||
}
|
||||
break;
|
||||
case 'positive':
|
||||
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||
if (isFieldTypeNumber(type)) {
|
||||
schema = schema.positive();
|
||||
}
|
||||
break;
|
||||
case 'negative':
|
||||
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||
if (isFieldTypeNumber(type)) {
|
||||
schema = schema.negative();
|
||||
}
|
||||
break;
|
||||
|
@ -0,0 +1,3 @@
|
||||
export default function isFieldTypeNumber(type) {
|
||||
return ['integer', 'biginteger', 'decimal', 'float', 'number'].includes(type);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import isFieldTypeNumber from '../isFieldTypeNumber';
|
||||
|
||||
const FIXTURE = [
|
||||
['integer', true],
|
||||
['float', true],
|
||||
['decimal', true],
|
||||
['biginteger', true],
|
||||
['number', true],
|
||||
['text', false],
|
||||
];
|
||||
|
||||
describe('isFieldTypeNumber', () => {
|
||||
FIXTURE.forEach(([type, expectation]) => {
|
||||
test(`${type} is ${expectation}`, () => {
|
||||
expect(isFieldTypeNumber(type)).toBe(expectation);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user