Skip to content

Commit

Permalink
Add "color" attribute format
Browse files Browse the repository at this point in the history
  • Loading branch information
TdyP authored Aug 21, 2023
1 parent d9f7b1c commit 0d8493b
Show file tree
Hide file tree
Showing 32 changed files with 337 additions and 177 deletions.
3 changes: 2 additions & 1 deletion apps/admin/public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
"date": "Date",
"date_range": "Period",
"encrypted": "Encrypted",
"numeric": "Numeric"
"numeric": "Numeric",
"color": "Color"
},
"values_list_enable": "Enable",
"allow_free_entry": "Allow free entry",
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/public/locales/fr/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
"date": "Date",
"date_range": "Période",
"encrypted": "Crypté",
"numeric": "Numérique"
"numeric": "Numérique",
"color": "Couleur"
},
"values_list_enable": "Activer",
"allow_free_entry": "Autoriser la saisie libre",
Expand Down
1 change: 1 addition & 0 deletions apps/admin/src/_gqlTypes/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export enum ApplicationType {

export enum AttributeFormat {
boolean = 'boolean',
color = 'color',
date = 'date',
date_range = 'date_range',
encrypted = 'encrypted',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function ReserveAttribute({attribute}: IReserveAttributeProps): JSX.Element {
const _getElement = () => {
const elemByFormat: {[format in AttributeFormat]: IUIElement} = {
[AttributeFormat.boolean]: formElements[FieldTypes.CHECKBOX],
[AttributeFormat.color]: formElements[FieldTypes.TEXT_INPUT],
[AttributeFormat.date]: formElements[FieldTypes.DATE],
[AttributeFormat.encrypted]: formElements[FieldTypes.ENCRYPTED],
[AttributeFormat.extended]: formElements[FieldTypes.TEXT_INPUT],
Expand Down
3 changes: 2 additions & 1 deletion apps/core/src/_types/attribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export enum AttributeFormats {
DATE_RANGE = 'date_range',
ENCRYPTED = 'encrypted',
BOOLEAN = 'boolean',
EXTENDED = 'extended'
EXTENDED = 'extended',
COLOR = 'color'
}

export enum IOTypes {
Expand Down
6 changes: 6 additions & 0 deletions apps/core/src/app/core/attributeApp/attributeApp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ describe('coreAttributeApp', () => {
);
});

test('Color attribute', async () => {
expect(await attrApp.getGraphQLFormat({...mockAttrSimple, format: AttributeFormats.COLOR})).toBe(
'String'
);
});

test('Simple link attribute', async () => {
expect(await attrApp.getGraphQLFormat({...mockAttrSimpleLink})).toBe('TestLib');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const getFormatFromAttribute = (format: AttributeFormats): string => {
switch (format) {
case AttributeFormats.TEXT:
case AttributeFormats.ENCRYPTED:
case AttributeFormats.COLOR:
return 'String';
case AttributeFormats.NUMERIC:
case AttributeFormats.DATE:
Expand Down
16 changes: 16 additions & 0 deletions apps/core/src/domain/actions/validateFormatAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('validateFormatAction', () => {
const attrNumeric = {...mockAttr, format: AttributeFormats.NUMERIC};
const attrDate = {...mockAttr, format: AttributeFormats.DATE};
const attrBoolean = {...mockAttr, format: AttributeFormats.BOOLEAN};
const attrColor = {...mockAttr, format: AttributeFormats.COLOR};
const attrExt = {
...mockAttr,
format: AttributeFormats.EXTENDED,
Expand Down Expand Up @@ -56,4 +57,19 @@ describe('validateFormatAction', () => {
const badExtValue = {street: 'test', city: {zipcode: 'aaa', name: 'Grenoble'}};
expect(() => action(badExtValue, {}, {attribute: attrExt})).toThrow(ValidationError);
});

test('validateFormat COLOR', async () => {
const colorValue = 'FFFFFF';
expect(action(colorValue, {}, {attribute: attrColor})).toBe(colorValue);
});

test('Throw if invalid format COLOR', async () => {
const badColorValue = 'AZERTY';
expect(() => action(badColorValue, {}, {attribute: attrColor})).toThrow(ValidationError);
});

test('Throw if invalid format COLOR, to be less or equal to 6 characters ', async () => {
const badColorValue = 'FFFFFFFFFFFFFFFFFFF';
expect(() => action(badColorValue, {}, {attribute: attrColor})).toThrow(ValidationError);
});
});
5 changes: 3 additions & 2 deletions apps/core/src/domain/actions/validateFormatAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export default function ({'core.domain.actionsList': actionsListDomain = null}:
to: Joi.date().timestamp('unix').raw().required()
});
break;
case AttributeFormats.COLOR:
schema = Joi.string().max(6).hex();
break;
}

return schema;
Expand All @@ -89,12 +92,10 @@ export default function ({'core.domain.actionsList': actionsListDomain = null}:
if (!attributeSchema) {
return value;
}

// Joi might convert value before testing. raw() force it to send back the value we passed in
const formatSchema = attributeSchema.raw();

const validationRes = formatSchema.validate(value);

if (!!validationRes.error) {
throw new ValidationError(actionsListDomain.handleJoiError(ctx.attribute, validationRes.error));
}
Expand Down
2 changes: 1 addition & 1 deletion apps/data-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@leav/ui": "workspace:libs/ui",
"@leav/utils": "workspace:libs/utils",
"@reduxjs/toolkit": "1.9.2",
"antd": "5.7.0",
"antd": "5.8.3",
"apollo-cache-inmemory": "1.6.6",
"apollo-upload-client": "14.1.3",
"dayjs": "1.11.7",
Expand Down
1 change: 1 addition & 0 deletions apps/data-studio/src/_gqlTypes/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum ApplicationType {

export enum AttributeFormat {
boolean = 'boolean',
color = 'color',
date = 'date',
date_range = 'date_range',
encrypted = 'encrypted',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ const allowedConditionByFormat: {[format in AttributeFormat]: AttributeCondition
AttributeConditionFilter.END_BEFORE,
AttributeConditionFilter.IS_EMPTY,
AttributeConditionFilter.IS_NOT_EMPTY
],
[AttributeFormat.color]: [
AttributeConditionFilter.CONTAINS,
AttributeConditionFilter.NOT_CONTAINS,
AttributeConditionFilter.EQUAL,
AttributeConditionFilter.NOT_EQUAL,
AttributeConditionFilter.IS_EMPTY,
AttributeConditionFilter.IS_NOT_EMPTY
]
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
import {CheckOutlined, CloseOutlined, FileTextOutlined} from '@ant-design/icons';
import {AnyPrimitive} from '@leav/utils';
import {Switch, Tooltip, Typography} from 'antd';
import {Switch, Tag, Tooltip, Typography} from 'antd';
import {useTranslation} from 'react-i18next';
import styled from 'styled-components';
import {stringifyDateRangeValue} from 'utils';
import {getInvertColor, stringifyDateRangeValue} from 'utils';
import {AttributeFormat} from '_gqlTypes/globalTypes';
import {IDateRangeValue, ITableCell} from '_types/types';
import {cp} from 'fs';
import {isEmpty} from 'lodash';

interface ISimpleCellProps {
cellData: ITableCell;
Expand All @@ -22,7 +24,8 @@ const alignmentByFormat: Record<AttributeFormat, 'left' | 'right' | 'center'> =
[AttributeFormat.date]: 'left',
[AttributeFormat.date_range]: 'left',
[AttributeFormat.extended]: 'left',
[AttributeFormat.encrypted]: 'left'
[AttributeFormat.encrypted]: 'left',
[AttributeFormat.color]: 'left'
};

const Wrapper = styled.div<{format: AttributeFormat}>`
Expand All @@ -46,6 +49,40 @@ function StandardCell({cellData, values}: ISimpleCellProps): JSX.Element {

const displayedValues = values.map(val => _getValueByFormat(val)).join(', ');

const _getTagStyle = (value: string) => {
return {color: getInvertColor('#' + value)};
};

const _getElementToDisplay = () => {
switch (cellData.format) {
case AttributeFormat.boolean:
return (
<Switch
disabled
checked={!!values[0]}
checkedChildren={<CheckOutlined />}
unCheckedChildren={<CloseOutlined />}
/>
);
case AttributeFormat.color:
if (!isEmpty(values)) {
return (
<>
{values.map(valueHex => (
<Tag bordered={true} color={'#' + valueHex} style={_getTagStyle('#' + valueHex)}>
{'#' + valueHex}
</Tag>
))}
</>
);
} else {
return displayedValues;
}
default:
return displayedValues;
}
};

return (
<Wrapper format={cellData.format}>
{cellData.format === AttributeFormat.extended ? (
Expand All @@ -60,16 +97,7 @@ function StandardCell({cellData, values}: ISimpleCellProps): JSX.Element {
}}
style={{margin: 0}}
>
{cellData.format === AttributeFormat.boolean ? (
<Switch
disabled
checked={!!values[0]}
checkedChildren={<CheckOutlined />}
unCheckedChildren={<CloseOutlined />}
/>
) : (
displayedValues
)}
{_getElementToDisplay()}
</Typography.Paragraph>
)}
</Wrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {mockModifier} from '__mocks__/common/value';
import {RECORD_FORM_recordForm_elements_attribute_StandardAttribute} from '_gqlTypes/RECORD_FORM';
import {SAVE_VALUE_BATCH_saveValueBatch_values_Value_attribute} from '_gqlTypes/SAVE_VALUE_BATCH';
import {AttributeFormat, AttributeType} from '_gqlTypes/globalTypes';
import {act, render, screen} from '_tests/testUtils';
import {act, render, screen, waitFor} from '_tests/testUtils';
import {
EditRecordReducerActionsTypes,
initialState
Expand Down Expand Up @@ -213,7 +213,6 @@ describe('StandardField', () => {

const inputElem = screen.getByRole('textbox');
userEvent.click(inputElem);

const calendarElem = screen.getByTestId('datepicker');
expect(calendarElem).toBeInTheDocument();

Expand All @@ -223,6 +222,46 @@ describe('StandardField', () => {
expect(mockHandleSubmit).toHaveBeenCalled();
});

test('Render color field', async () => {
const colorValue = 'FFFFFF';
const newColorValue = '000000';
const recordValuesDate = [
{
...mockRecordValuesCommon,
value: colorValue,
raw_value: colorValue
}
];
render(
<StandardField
element={{
...mockFormElementInput,
attribute: {...mockFormAttribute, format: AttributeFormat.color},
values: recordValuesDate
}}
{...baseProps}
/>
);
// Open ColorPicker Element
const colorElem = screen.getByRole('textbox');
await act(async () => {
userEvent.click(colorElem);
});

const colorPickerElem = screen.getByRole('textbox');
expect(colorPickerElem).toBeInTheDocument();

// Update color value
userEvent.clear(colorPickerElem);
userEvent.type(colorPickerElem, newColorValue);
await act(async () => {
userEvent.click(screen.getByRole('button', {name: 'global.submit'}));
});

expect(colorPickerElem).not.toBeInTheDocument();
expect(mockHandleSubmit).toHaveBeenCalled();
});

test('Render checkbox', async () => {
const recordValuesBoolean = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright LEAV Solutions 2017
// This file is released under LGPL V3
// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
import {ColorPicker} from 'antd';
import {Color} from 'antd/es/color-picker';
import {IStandardInputProps} from 'components/RecordEdition/EditRecord/_types';

function ColorInput({state, fieldValue, onChange, onFocus}: IStandardInputProps): JSX.Element {
const {editingValue} = fieldValue;
const colorValue = '#' + editingValue.toString();

const _handleColorChange = (value: Color) => {
onChange(value.toHex().toUpperCase());
};
const colorPickerStyle: React.CSSProperties = {
height: fieldValue.index ? '44px' : '54px',
width: '100%',
justifyContent: 'left',
paddingTop: fieldValue.index ? '4px' : '14px',
paddingLeft: '15px'
};

return (
<ColorPicker
showText
disabledAlpha
disabled={state.isReadOnly}
value={colorValue}
style={colorPickerStyle}
open={fieldValue.isEditing}
size="small"
onChangeComplete={_handleColorChange}
onOpenChange={onFocus}
/>
);
}

export default ColorInput;
Loading

0 comments on commit 0d8493b

Please sign in to comment.