From 2a96aef3a6c1667b991a79d7b055fd168651483a Mon Sep 17 00:00:00 2001 From: Jules HABLOT Date: Tue, 10 Dec 2024 11:51:04 +0100 Subject: [PATCH] feat(@leav/ui): manage display multi color attribute --- libs/ui/src/components/Explorer/DataView.tsx | 1 + .../src/components/Explorer/Explorer.test.tsx | 53 +++++++++++++++++-- libs/ui/src/components/Explorer/TableCell.tsx | 22 ++++++-- .../src/components/Explorer/TableTagGroup.tsx | 9 ++++ 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/libs/ui/src/components/Explorer/DataView.tsx b/libs/ui/src/components/Explorer/DataView.tsx index c57d9bbfc..cf550e980 100644 --- a/libs/ui/src/components/Explorer/DataView.tsx +++ b/libs/ui/src/components/Explorer/DataView.tsx @@ -148,6 +148,7 @@ export const DataView: FunctionComponent = ({ borderedRows cellsBackgroundColor={theme.utilities.light} backgroundColor={theme.colors.primary['50']} + showHeader={dataGroupedFilteredSorted.length > 0} columns={columns} scroll={{y: scrollHeight}} dataSource={dataGroupedFilteredSorted} diff --git a/libs/ui/src/components/Explorer/Explorer.test.tsx b/libs/ui/src/components/Explorer/Explorer.test.tsx index dac05e996..d9b55bb48 100644 --- a/libs/ui/src/components/Explorer/Explorer.test.tsx +++ b/libs/ui/src/components/Explorer/Explorer.test.tsx @@ -96,6 +96,16 @@ const simpleColorMockAttribute = { } } satisfies gqlTypes.AttributePropertiesFragment; +const multivalColorMockAttribute = { + ...simpleColorMockAttribute, + id: 'color_multival', + multiple_values: true, + label: { + fr: 'Mon attribut couleur multiple', + en: 'My color attribute multi-valued' + } +} satisfies gqlTypes.AttributePropertiesFragment; + describe('Explorer', () => { const recordId1 = '613982168'; const enrichTextRecord1 = '

This is a test enrich text

'; @@ -196,6 +206,11 @@ describe('Explorer', () => { } ] }, + { + attributeId: multivalColorMockAttribute.id, + attributeProperties: multivalColorMockAttribute, + values: [{valuePayload: '00FF00'}, {valuePayload: 'FF0000'}, {valuePayload: '0000FF'}] + }, { attributeId: booleanMockAttribute.id, attributeProperties: booleanMockAttribute, @@ -277,6 +292,11 @@ describe('Explorer', () => { } ] }, + { + attributeId: multivalColorMockAttribute.id, + attributeProperties: multivalColorMockAttribute, + values: [] + }, { attributeId: booleanMockAttribute.id, attributeProperties: booleanMockAttribute, @@ -291,6 +311,16 @@ describe('Explorer', () => { } ] satisfies gqlTypes.ExplorerQuery['records']['list']; + const mockEmptyExplorerQueryResult: Mockify = { + loading: false, + called: true, + data: { + records: { + list: [] + } + } + }; + const mockExplorerQueryResult: Mockify = { loading: false, called: true, @@ -334,10 +364,12 @@ describe('Explorer', () => { ]; const [customPrimaryAction1, customPrimaryAction2] = customPrimaryActions; + let spyUseExplorerQuery: jest.SpyInstance; + beforeAll(() => { - jest.spyOn(gqlTypes, 'useExplorerQuery').mockImplementation( - () => mockExplorerQueryResult as gqlTypes.ExplorerQueryResult - ); + spyUseExplorerQuery = jest + .spyOn(gqlTypes, 'useExplorerQuery') + .mockImplementation(() => mockExplorerQueryResult as gqlTypes.ExplorerQueryResult); jest.spyOn(gqlTypes, 'useExplorerLibraryDataQuery').mockImplementation( () => mockLibraryDataQueryResult as gqlTypes.ExplorerLibraryDataQueryResult @@ -374,6 +406,15 @@ describe('Explorer', () => { expect(screen.getByText(record2.whoAmI.label)).toBeInTheDocument(); }); + test('Should hide table header when no data provided', async () => { + spyUseExplorerQuery.mockReturnValueOnce(mockEmptyExplorerQueryResult); + render(); + + expect(screen.getByRole('table')).toBeVisible(); + expect(screen.getByRole('row')).toBeVisible(); + expect(within(screen.getByRole('row')).getByText('Aucune donnée')).toBeVisible(); + }); + test('Should display the list of records in a table with attributes values', async () => { render( { multivalLinkMockAttribute.id, simpleRichTextMockAttribute.id, simpleColorMockAttribute.id, + multivalColorMockAttribute.id, booleanMockAttribute.id, multivalBooleanMockAttribute.id ] @@ -404,6 +446,7 @@ describe('Explorer', () => { multivalLinkCell, simpleRichTextCell, simpleColorCell, + multivalColorCell, boolCell, multivalBoolCell ] = within(firstRecordRow).getAllByRole('cell'); @@ -426,6 +469,10 @@ describe('Explorer', () => { expect(simpleColorCell).toHaveTextContent(`#${colorRecord1}`); + expect(within(multivalColorCell).getByText('#00FF00')).toBeVisible(); + expect(within(multivalColorCell).getByText('#FF0000')).toBeVisible(); + expect(within(multivalColorCell).getByText('#0000FF')).toBeVisible(); + expect(within(boolCell).getByText(/yes/)).toBeVisible(); expect(within(multivalBoolCell).getByText(/yes/)).toBeVisible(); diff --git a/libs/ui/src/components/Explorer/TableCell.tsx b/libs/ui/src/components/Explorer/TableCell.tsx index afb455036..8a461324b 100644 --- a/libs/ui/src/components/Explorer/TableCell.tsx +++ b/libs/ui/src/components/Explorer/TableCell.tsx @@ -1,6 +1,7 @@ // Copyright LEAV Solutions 2017 until 2023/11/05, Copyright Aristid from 2023/11/06 // This file is released under LGPL V3 // License text available at https://www.gnu.org/licenses/lgpl-3.0.txt +import {FunctionComponent, ReactNode} from 'react'; import { AttributeFormat, AttributePropertiesFragment, @@ -11,14 +12,13 @@ import { PropertyValueValueFragment } from '_ui/_gqlTypes'; import {useSharedTranslation} from '_ui/hooks/useSharedTranslation'; +import {FaListAlt} from 'react-icons/fa'; +import DOMPurify from 'dompurify'; import {KitAvatar, KitTag, KitTypography} from 'aristid-ds'; -import {FunctionComponent, ReactNode} from 'react'; +import {IKitTag, IKitTagConfig} from 'aristid-ds/dist/Kit/DataDisplay/Tag/types'; import styled from 'styled-components'; import {IdCard} from './IdCard'; -import {IKitTag, IKitTagConfig} from 'aristid-ds/dist/Kit/DataDisplay/Tag/types'; -import {TableTagGroup} from './TableTagGroup'; -import {FaListAlt} from 'react-icons/fa'; -import DOMPurify from 'dompurify'; +import {multiColorTagAvatarClassName, TableTagGroup} from './TableTagGroup'; const isStandardValue = ( v: PropertyValueFragment, @@ -80,6 +80,18 @@ export const TableCell: FunctionComponent = ({values, attribute }, type: value.valuePayload ? 'primary' : ('neutral' as IKitTag['type']) }; + case AttributeFormat.color: + const colorHexa = `#${value.valuePayload}`; + return { + idCardProps: { + description: colorHexa, + avatarProps: { + color: colorHexa, + shape: 'square', + className: multiColorTagAvatarClassName + } + } + }; default: const valueContent = attributeProperties.format === AttributeFormat.encrypted diff --git a/libs/ui/src/components/Explorer/TableTagGroup.tsx b/libs/ui/src/components/Explorer/TableTagGroup.tsx index 0fbffe391..286dce07f 100644 --- a/libs/ui/src/components/Explorer/TableTagGroup.tsx +++ b/libs/ui/src/components/Explorer/TableTagGroup.tsx @@ -6,6 +6,8 @@ import {IKitTag} from 'aristid-ds/dist/Kit/DataDisplay/Tag/types'; import {FunctionComponent} from 'react'; import styled from 'styled-components'; +export const multiColorTagAvatarClassName = 'multi-color-tag-avatar'; + const StyledTagsGroupDiv = styled.div` display: flex; column-gap: calc(var(--general-spacing-xxs) * 1px); @@ -15,6 +17,13 @@ const StyledTagsGroupDiv = styled.div` & > span { margin-right: var(--general-spacing-none); } + + // TODO: wait DS to allow better customization on avatar + &&& .${multiColorTagAvatarClassName} { + height: calc(var(--general-spacing-s) * 1px); + width: calc(var(--general-spacing-s) * 1px); + border-radius: calc(var(--general-border-radius-xs) * 1px); + } `; export const TableTagGroup: FunctionComponent<{