From 28ca9a9e498b1524363463d2235144dba7367f3c Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Wed, 6 Sep 2023 16:41:26 +0200 Subject: [PATCH] Refactor/new menu item (#1448) * wip * finished * Added disabled * Fixed disabled * Finished cleaning * Minor fixes from merge * Added docs * Added PascalCase * Fix from review * Fixes from merge * Fix lint * Fixed storybook tests --- .../developer/frontend/best-practices.mdx | 29 +++++++ front/package.json | 1 + .../activities/components/ActivityEditor.tsx | 2 +- .../ActivityAssigneeEditableField.tsx | 2 +- .../ActivityRelationEditableField.tsx | 2 +- .../companies/components/CompanyBoardCard.tsx | 2 +- .../components/CompanyProgressPicker.tsx | 16 ++-- .../companiesAvailableColumnDefinitions.tsx | 20 ++--- .../useCompanyTableContextMenuEntries.tsx | 6 +- .../hooks/useUpdateCompanyBoardColumns.ts | 23 +++-- .../companies/utils/fieldsForCompany.tsx | 16 ++-- .../modules/people/components/PeopleCard.tsx | 28 +++--- .../peopleAvailableColumnDefinitions.tsx | 18 ++-- .../usePeopleTableContextMenuEntries.tsx | 6 +- .../modules/people/utils/fieldsForPerson.tsx | 19 ++-- .../pipelineAvailableFieldDefinitions.tsx | 8 +- .../pipeline/hooks/usePipelineStages.ts | 2 +- .../components/MatchColumnSelect.tsx | 85 ++++-------------- .../components/TemplateColumn.tsx | 2 +- .../ValidationStep/components/columns.tsx | 2 +- .../spreadsheet-import/tests/mockRsiValues.ts | 4 +- .../modules/spreadsheet-import/types/index.ts | 5 +- .../ui/board/components/BoardColumn.tsx | 7 +- .../components/BoardColumnEditTitleMenu.tsx | 32 +++---- .../ui/board/components/BoardColumnMenu.tsx | 36 ++++---- .../BoardOptionsDropdownContent.tsx | 43 +++++---- .../BoardColumnEditTitleMenu.stories.tsx | 4 +- .../hooks/useBoardContextMenuEntries.tsx | 2 +- .../ui/board/types/BoardColumnDefinition.ts | 4 +- .../components/ContextMenuEntry.tsx | 24 ++--- .../components/DropdownMenuCheckableItem.tsx | 52 ----------- .../components/DropdownMenuHeader.tsx | 27 ++++-- .../dropdown/components/DropdownMenuItem.tsx | 79 ----------------- .../components/DropdownMenuSelectableItem.tsx | 87 ------------------- .../StyledDropdownMenuItemsContainer.tsx | 1 + .../__stories__/DropdownMenu.stories.tsx | 87 +++++++++---------- .../components/EditableField.tsx | 11 ++- .../GenericEditableBooleanField.tsx | 2 +- .../components/GenericEditableDateField.tsx | 2 +- .../components/GenericEditableNumberField.tsx | 2 +- .../components/GenericEditablePhoneField.tsx | 2 +- .../GenericEditableRelationField.tsx | 2 +- .../components/GenericEditableTextField.tsx | 2 +- .../components/GenericEditableURLField.tsx | 2 +- .../components/ProbabilityEditableField.tsx | 2 +- .../editable-field/types/FieldDefinition.ts | 4 +- .../ui/editable-field/types/ViewField.ts | 3 +- .../variants/components/DateEditableField.tsx | 7 +- .../components/PhoneEditableField.tsx | 7 +- .../__stories__/DateEditableField.stories.tsx | 6 +- .../PhoneEditableField.stories.tsx | 6 +- .../components/FilterDropdownFilterSelect.tsx | 12 +-- .../FilterDropdownOperandButton.tsx | 5 +- .../FilterDropdownOperandSelect.tsx | 9 +- .../components/SortAndFilterBar.tsx | 16 ++-- .../components/SortDropdownButton.tsx | 25 +++--- .../components/SortOrFilterChip.tsx | 18 ++-- .../filter-n-sort/types/FilterDefinition.ts | 4 +- .../ui/filter-n-sort/types/interface.ts | 5 +- .../modules/ui/icon/types/IconComponent.ts | 4 +- .../components/MultipleEntitySelect.tsx | 35 ++++---- .../components/SingleEntitySelect.tsx | 10 +-- .../components/SingleEntitySelectBase.tsx | 57 ++++++------ .../components/ShowPageAddButton.tsx | 26 +++--- .../ui/menu-item/components/MenuItem.tsx | 22 +++-- .../components/MenuItemMultiSelectAvatar.tsx | 51 +++++++++++ .../menu-item/components/MenuItemNavigate.tsx | 2 +- .../menu-item/components/MenuItemSelect.tsx | 33 ++++++- .../components/MenuItemSelectAvatar.tsx | 55 ++++++++++++ .../components/MenuItemSelectColor.tsx | 59 +++++++++++++ .../components/MenuItemLeftContent.tsx | 7 +- .../components/StyledMenuItemBase.tsx | 11 ++- .../ui/menu-item/types/MenuItemAccent.ts | 2 +- .../ui/table/components/ColumnHead.tsx | 13 ++- .../components/EntityTableColumnMenu.tsx | 30 +++---- .../ui/table/components/EntityTableHeader.tsx | 2 +- .../TableOptionsDropdownContent.tsx | 72 ++++----------- .../TableOptionsDropdownSection.tsx | 40 ++++----- .../components/TableUpdateViewButtonGroup.tsx | 14 ++- .../components/TableViewsDropdownButton.tsx | 47 +++++----- front/src/modules/ui/tag/components/Tag.tsx | 4 +- .../src/modules/ui/theme/constants/colors.ts | 39 ++++++--- .../ui/theme/utils/castStringAsThemeColor.ts | 7 ++ .../Companies.filterBy.stories.tsx | 24 ++--- .../__stories__/Companies.sortBy.stories.tsx | 5 +- .../src/pages/companies/companies-filters.tsx | 15 ++-- front/src/pages/companies/companies-sorts.tsx | 10 +-- .../constants/companyShowFieldDefinition.tsx | 14 +-- .../opportunities/opportunities-filters.tsx | 11 +-- .../opportunities/opportunities-sorts.tsx | 6 +- .../__stories__/People.filterBy.stories.tsx | 14 +-- .../__stories__/People.sortBy.stories.tsx | 8 +- .../constants/personShowFieldDefinition.tsx | 16 ++-- front/src/pages/people/people-filters.tsx | 17 ++-- front/src/pages/people/people-sorts.tsx | 13 ++- front/src/pages/tasks/tasks-filters.tsx | 2 +- 96 files changed, 816 insertions(+), 918 deletions(-) delete mode 100644 front/src/modules/ui/dropdown/components/DropdownMenuCheckableItem.tsx delete mode 100644 front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx delete mode 100644 front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx create mode 100644 front/src/modules/ui/menu-item/components/MenuItemMultiSelectAvatar.tsx create mode 100644 front/src/modules/ui/menu-item/components/MenuItemSelectAvatar.tsx create mode 100644 front/src/modules/ui/menu-item/components/MenuItemSelectColor.tsx create mode 100644 front/src/modules/ui/theme/utils/castStringAsThemeColor.ts diff --git a/docs/docs/developer/frontend/best-practices.mdx b/docs/docs/developer/frontend/best-practices.mdx index 3e1d2cf814b..c31df21dd38 100644 --- a/docs/docs/developer/frontend/best-practices.mdx +++ b/docs/docs/developer/frontend/best-practices.mdx @@ -236,6 +236,35 @@ function Form() { } ``` +## Component as props + +Try as much as possible to pass uninstanciated components as props, so chilren can decide on their own of what props they need to pass. + +The most common example for that is icon components : + +```tsx +function SomeParentComponent() { + return ( + + ) +} + +// In MyComponent +function MyComponent({ MyIcon }: { MyIcon: IconComponent }) { + const theme = useTheme(); + + return ( +
+ +
+ ) +} +``` + +For React to understand that the component is a component, you need to use PascalCase, to later instanciate it with `` + +```tsx + ## Prop Drilling: Keep It Minimal Prop drilling, in the React context, refers to the practice of passing state variables and their setters through multiple component layers, even if intermediary components don't use them. While sometimes necessary, excessive prop drilling can lead to: diff --git a/front/package.json b/front/package.json index d1372bacb6d..2fe5df664ea 100644 --- a/front/package.json +++ b/front/package.json @@ -71,6 +71,7 @@ "lint": "eslint src --max-warnings=0", "storybook:dev": "storybook dev -p 6006 -s ../public", "storybook:test": "test-storybook", + "storybook:test-slow": "test-storybook --maxWorkers=3", "storybook:build": "storybook build -s public", "storybook:coverage": "test-storybook --coverage --maxWorkers=3 && npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook --check-coverage", "graphql:generate": "dotenv cross-var graphql-codegen --config codegen.js", diff --git a/front/src/modules/activities/components/ActivityEditor.tsx b/front/src/modules/activities/components/ActivityEditor.tsx index 5d01a62653b..826893f8c62 100644 --- a/front/src/modules/activities/components/ActivityEditor.tsx +++ b/front/src/modules/activities/components/ActivityEditor.tsx @@ -185,7 +185,7 @@ export function ActivityEditor({ <> } + Icon={IconCalendar} label="Due date" onSubmit={(newDate) => { updateActivityMutation({ diff --git a/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx b/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx index a5d9f7e0075..61ae4cb32a2 100644 --- a/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx +++ b/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx @@ -23,7 +23,7 @@ export function ActivityAssigneeEditableField({ activity }: OwnProps) { scope: RelationPickerHotkeyScope.RelationPicker, }} label="Assignee" - iconLabel={} + IconLabel={IconUserCircle} editModeContent={ } diff --git a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableField.tsx b/front/src/modules/activities/editable-fields/components/ActivityRelationEditableField.tsx index 8a55265114e..a096d20e318 100644 --- a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableField.tsx +++ b/front/src/modules/activities/editable-fields/components/ActivityRelationEditableField.tsx @@ -28,7 +28,7 @@ export function ActivityRelationEditableField({ activity }: OwnProps) { customEditHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker, }} - iconLabel={} + IconLabel={IconArrowUpRight} editModeContent={ } diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx index d75dab85ae9..77d2d9d03d5 100644 --- a/front/src/modules/companies/components/CompanyBoardCard.tsx +++ b/front/src/modules/companies/components/CompanyBoardCard.tsx @@ -167,7 +167,7 @@ export function CompanyBoardCard() { value={{ key: viewField.key, name: viewField.name, - icon: viewField.icon, + Icon: viewField.Icon, type: viewField.metadata.type, metadata: viewField.metadata, }} diff --git a/front/src/modules/companies/components/CompanyProgressPicker.tsx b/front/src/modules/companies/components/CompanyProgressPicker.tsx index 26ddecb0d2f..4ff2c92fc93 100644 --- a/front/src/modules/companies/components/CompanyProgressPicker.tsx +++ b/front/src/modules/companies/components/CompanyProgressPicker.tsx @@ -1,11 +1,9 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useTheme } from '@emotion/react'; import { useRecoilState } from 'recoil'; import { currentPipelineState } from '@/pipeline/states/currentPipelineState'; import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; @@ -13,6 +11,7 @@ import { IconChevronDown } from '@/ui/icon'; import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { useEntitySelectSearch } from '@/ui/input/relation-picker/hooks/useEntitySelectSearch'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery'; @@ -47,8 +46,6 @@ export function CompanyProgressPicker({ string | null >(null); - const theme = useTheme(); - const [currentPipeline] = useRecoilState(currentPipelineState); const currentPipelineStages = useMemo( @@ -89,22 +86,21 @@ export function CompanyProgressPicker({ {isProgressSelectionUnfolded ? ( {currentPipelineStages.map((pipelineStage, index) => ( - { handlePipelineStageChange(pipelineStage.id); }} - > - {pipelineStage.name} - + text={pipelineStage.name} + /> ))} ) : ( <> } + EndIcon={IconChevronDown} onClick={() => setIsProgressSelectionUnfolded(true)} > {selectedPipelineStage?.name} diff --git a/front/src/modules/companies/constants/companiesAvailableColumnDefinitions.tsx b/front/src/modules/companies/constants/companiesAvailableColumnDefinitions.tsx index 6ea68721a43..362d33af737 100644 --- a/front/src/modules/companies/constants/companiesAvailableColumnDefinitions.tsx +++ b/front/src/modules/companies/constants/companiesAvailableColumnDefinitions.tsx @@ -29,7 +29,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBuildingSkyscraper, size: 180, index: 0, metadata: { @@ -43,7 +43,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconLink, size: 100, index: 1, metadata: { @@ -56,7 +56,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconUserCircle, size: 150, index: 2, metadata: { @@ -69,7 +69,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconCalendarEvent, size: 150, index: 3, metadata: { @@ -81,7 +81,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconUsers, size: 150, index: 4, metadata: { @@ -94,7 +94,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBrandLinkedin, size: 170, index: 5, metadata: { @@ -107,7 +107,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconMap, size: 170, index: 6, metadata: { @@ -120,7 +120,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconTarget, size: 150, index: 7, metadata: { @@ -132,7 +132,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconMoneybag, size: 150, index: 8, metadata: { @@ -143,7 +143,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBrandX, size: 150, index: 9, metadata: { diff --git a/front/src/modules/companies/hooks/useCompanyTableContextMenuEntries.tsx b/front/src/modules/companies/hooks/useCompanyTableContextMenuEntries.tsx index 5cecff82739..7dbf90f288c 100644 --- a/front/src/modules/companies/hooks/useCompanyTableContextMenuEntries.tsx +++ b/front/src/modules/companies/hooks/useCompanyTableContextMenuEntries.tsx @@ -26,19 +26,19 @@ export function useCompanyTableContextMenuEntries() { setContextMenuEntries([ } + Icon={IconNotes} onClick={() => handleButtonClick(ActivityType.Note)} key="note" />, } + Icon={IconCheckbox} onClick={() => handleButtonClick(ActivityType.Task)} key="task" />, } + Icon={IconTrash} accent="danger" onClick={() => deleteSelectedCompanies()} key="delete" diff --git a/front/src/modules/companies/hooks/useUpdateCompanyBoardColumns.ts b/front/src/modules/companies/hooks/useUpdateCompanyBoardColumns.ts index 5ec6348dfcf..85002c8fb71 100644 --- a/front/src/modules/companies/hooks/useUpdateCompanyBoardColumns.ts +++ b/front/src/modules/companies/hooks/useUpdateCompanyBoardColumns.ts @@ -5,6 +5,7 @@ import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardId import { boardColumnsState } from '@/ui/board/states/boardColumnsState'; import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition'; import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState'; +import { isThemeColor } from '@/ui/theme/utils/castStringAsThemeColor'; import { Pipeline } from '~/generated/graphql'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; @@ -97,12 +98,22 @@ export function useUpdateCompanyBoard() { }); const newBoardColumns: BoardColumnDefinition[] = - orderedPipelineStages?.map((pipelineStage) => ({ - id: pipelineStage.id, - title: pipelineStage.name, - colorCode: pipelineStage.color, - index: pipelineStage.index ?? 0, - })); + orderedPipelineStages?.map((pipelineStage) => { + if (!isThemeColor(pipelineStage.color)) { + console.warn( + `Color ${pipelineStage.color} is not recognized in useUpdateCompanyBoard.`, + ); + } + + return { + id: pipelineStage.id, + title: pipelineStage.name, + colorCode: isThemeColor(pipelineStage.color) + ? pipelineStage.color + : undefined, + index: pipelineStage.index ?? 0, + }; + }); if (!isDeeplyEqual(currentBoardColumns, newBoardColumns)) { set(boardColumnsState, newBoardColumns); diff --git a/front/src/modules/companies/utils/fieldsForCompany.tsx b/front/src/modules/companies/utils/fieldsForCompany.tsx index 991d94e82f3..2287a09186c 100644 --- a/front/src/modules/companies/utils/fieldsForCompany.tsx +++ b/front/src/modules/companies/utils/fieldsForCompany.tsx @@ -11,7 +11,7 @@ import { export const fieldsForCompany = [ { - icon: , + icon: IconBuildingSkyscraper, label: 'Name', key: 'name', alternateMatches: ['name', 'company name', 'company'], @@ -21,7 +21,7 @@ export const fieldsForCompany = [ example: 'Tim', }, { - icon: , + icon: IconMail, label: 'Domain name', key: 'domainName', alternateMatches: ['domain', 'domain name'], @@ -31,7 +31,7 @@ export const fieldsForCompany = [ example: 'apple.dev', }, { - icon: , + icon: IconBrandLinkedin, label: 'Linkedin URL', key: 'linkedinUrl', alternateMatches: ['linkedIn', 'linkedin', 'linkedin url'], @@ -41,7 +41,7 @@ export const fieldsForCompany = [ example: 'https://www.linkedin.com/in/apple', }, { - icon: , + icon: IconMoneybag, label: 'ARR', key: 'annualRecurringRevenue', alternateMatches: [ @@ -64,7 +64,7 @@ export const fieldsForCompany = [ example: '1000000', }, { - icon: , + icon: IconTarget, label: 'ICP', key: 'idealCustomerProfile', alternateMatches: [ @@ -86,7 +86,7 @@ export const fieldsForCompany = [ example: 'true/false', }, { - icon: , + icon: IconBrandX, label: 'x URL', key: 'xUrl', alternateMatches: ['x', 'twitter', 'twitter url', 'x url'], @@ -96,7 +96,7 @@ export const fieldsForCompany = [ example: 'https://x.com/tim_cook', }, { - icon: , + icon: IconMap, label: 'Address', key: 'address', fieldType: { @@ -105,7 +105,7 @@ export const fieldsForCompany = [ example: 'Maple street', }, { - icon: , + icon: IconUsers, label: 'Employees', key: 'employees', alternateMatches: ['employees', 'total employees', 'number of employees'], diff --git a/front/src/modules/people/components/PeopleCard.tsx b/front/src/modules/people/components/PeopleCard.tsx index ac053e7e81c..fbb9e36ca78 100644 --- a/front/src/modules/people/components/PeopleCard.tsx +++ b/front/src/modules/people/components/PeopleCard.tsx @@ -1,15 +1,14 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { getOperationName } from '@apollo/client/utilities'; -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react'; import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { IconDotsVertical, IconLinkOff, IconTrash } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { Avatar } from '@/users/components/Avatar'; import { @@ -72,10 +71,6 @@ const StyledJobTitle = styled.div` } `; -const StyledRemoveOption = styled.div` - color: ${({ theme }) => theme.color.red}; -`; - export function PeopleCard({ person, hasBottomBorder = true, @@ -93,8 +88,6 @@ export function PeopleCard({ placement: 'right-start', }); - const theme = useTheme(); - useListenClickOutside({ refs: [refs.floating], callback: () => { @@ -175,14 +168,17 @@ export function PeopleCard({ e.stopPropagation()} > - - - Detach relation - - - - Delete person - + + )} diff --git a/front/src/modules/people/constants/peopleAvailableColumnDefinitions.tsx b/front/src/modules/people/constants/peopleAvailableColumnDefinitions.tsx index 8861b7a4c57..b98fa47eef0 100644 --- a/front/src/modules/people/constants/peopleAvailableColumnDefinitions.tsx +++ b/front/src/modules/people/constants/peopleAvailableColumnDefinitions.tsx @@ -27,7 +27,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconUser, size: 210, index: 0, metadata: { @@ -43,7 +43,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconMail, size: 150, index: 1, metadata: { @@ -55,7 +55,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBuildingSkyscraper, size: 150, index: 2, metadata: { @@ -67,7 +67,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconPhone, size: 150, index: 3, metadata: { @@ -79,7 +79,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconCalendarEvent, size: 150, index: 4, metadata: { @@ -90,7 +90,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconMap, size: 150, index: 5, metadata: { @@ -102,7 +102,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBriefcase, size: 150, index: 6, metadata: { @@ -114,7 +114,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBrandLinkedin, size: 150, index: 7, metadata: { @@ -126,7 +126,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition, + Icon: IconBrandX, size: 150, index: 8, metadata: { diff --git a/front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx b/front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx index 8862d552f22..b7341f86e18 100644 --- a/front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx +++ b/front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx @@ -60,19 +60,19 @@ export function usePersonTableContextMenuEntries() { setContextMenuEntries([ } + Icon={IconNotes} onClick={() => handleActivityClick(ActivityType.Note)} key="note" />, } + Icon={IconCheckbox} onClick={() => handleActivityClick(ActivityType.Task)} key="task" />, } + Icon={IconTrash} accent="danger" onClick={handleDeleteClick} key="delete" diff --git a/front/src/modules/people/utils/fieldsForPerson.tsx b/front/src/modules/people/utils/fieldsForPerson.tsx index 2b163195741..53f0aec8846 100644 --- a/front/src/modules/people/utils/fieldsForPerson.tsx +++ b/front/src/modules/people/utils/fieldsForPerson.tsx @@ -1,5 +1,6 @@ import { isValidPhoneNumber } from 'libphonenumber-js'; +import { Fields } from '@/spreadsheet-import/types'; import { IconBrandLinkedin, IconBrandX, @@ -11,7 +12,7 @@ import { export const fieldsForPerson = [ { - icon: , + icon: IconUser, label: 'Firstname', key: 'firstName', alternateMatches: ['first name', 'first', 'firstname'], @@ -21,7 +22,7 @@ export const fieldsForPerson = [ example: 'Tim', }, { - icon: , + icon: IconUser, label: 'Lastname', key: 'lastName', alternateMatches: ['last name', 'last', 'lastname'], @@ -31,7 +32,7 @@ export const fieldsForPerson = [ example: 'Cook', }, { - icon: , + icon: IconMail, label: 'Email', key: 'email', alternateMatches: ['email', 'mail'], @@ -41,7 +42,7 @@ export const fieldsForPerson = [ example: 'tim@apple.dev', }, { - icon: , + icon: IconBrandLinkedin, label: 'Linkedin URL', key: 'linkedinUrl', alternateMatches: ['linkedIn', 'linkedin', 'linkedin url'], @@ -51,7 +52,7 @@ export const fieldsForPerson = [ example: 'https://www.linkedin.com/in/timcook', }, { - icon: , + icon: IconBrandX, label: 'X URL', key: 'xUrl', alternateMatches: ['x', 'x url'], @@ -61,7 +62,7 @@ export const fieldsForPerson = [ example: 'https://x.com/tim_cook', }, { - icon: , + icon: IconBriefcase, label: 'Job title', key: 'jobTitle', alternateMatches: ['job', 'job title'], @@ -71,7 +72,7 @@ export const fieldsForPerson = [ example: 'CEO', }, { - icon: , + icon: IconBriefcase, label: 'Phone', key: 'phone', fieldType: { @@ -88,7 +89,7 @@ export const fieldsForPerson = [ ], }, { - icon: , + icon: IconMap, label: 'City', key: 'city', fieldType: { @@ -96,4 +97,4 @@ export const fieldsForPerson = [ }, example: 'Seattle', }, -] as const; +] as Fields; diff --git a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx b/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx index 20f87db1345..0e1cb67c479 100644 --- a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx +++ b/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx @@ -19,7 +19,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition, + Icon: IconCalendarEvent, metadata: { type: 'date', fieldName: 'closeDate', @@ -29,7 +29,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition, + Icon: IconCurrencyDollar, metadata: { type: 'number', fieldName: 'amount', @@ -39,7 +39,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition, + Icon: IconProgressCheck, metadata: { type: 'probability', fieldName: 'probability', @@ -49,7 +49,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition, + Icon: IconUser, metadata: { type: 'relation', fieldName: 'pointOfContact', diff --git a/front/src/modules/pipeline/hooks/usePipelineStages.ts b/front/src/modules/pipeline/hooks/usePipelineStages.ts index db73c771793..81b73a02466 100644 --- a/front/src/modules/pipeline/hooks/usePipelineStages.ts +++ b/front/src/modules/pipeline/hooks/usePipelineStages.ts @@ -22,7 +22,7 @@ export const usePipelineStages = () => { return createPipelineStageMutation({ variables: { data: { - color: boardColumn.colorCode, + color: boardColumn.colorCode ?? 'gray', id: boardColumn.id, index: boardColumn.index, name: boardColumn.title, diff --git a/front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx b/front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx index 48a16a58f85..2d5365f76ee 100644 --- a/front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx +++ b/front/src/modules/spreadsheet-import/components/MatchColumnSelect.tsx @@ -14,44 +14,15 @@ import { ReadonlyDeep } from 'type-fest'; import type { SelectOption } from '@/spreadsheet-import/types'; import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; -import { IconChevronDown, TablerIconsProps } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemSelect } from '@/ui/menu-item/components/MenuItemSelect'; import { AppTooltip } from '@/ui/tooltip/AppTooltip'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useUpdateEffect } from '~/hooks/useUpdateEffect'; -const StyledDropdownItem = styled.div` - align-items: center; - background-color: ${({ theme }) => theme.background.tertiary}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - box-sizing: border-box; - display: flex; - flex-direction: row; - height: 32px; - padding-left: ${({ theme }) => theme.spacing(2)}; - padding-right: ${({ theme }) => theme.spacing(2)}; - width: 100%; - - &:hover { - background-color: ${({ theme }) => theme.background.quaternary}; - } -`; - -const StyledDropdownLabel = styled.span<{ isPlaceholder: boolean }>` - color: ${({ theme, isPlaceholder }) => - isPlaceholder ? theme.font.color.tertiary : theme.font.color.primary}; - display: flex; - flex: 1; - font-size: ${({ theme }) => theme.font.size.sm}; - font-weight: ${({ theme }) => theme.font.weight.regular}; - padding-left: ${({ theme }) => theme.spacing(1)}; - padding-right: ${({ theme }) => theme.spacing(1)}; -`; - const StyledFloatingDropdown = styled.div` z-index: ${({ theme }) => theme.lastLayerZIndex}; `; @@ -69,11 +40,9 @@ export const MatchColumnSelect = ({ value, options: initialOptions, placeholder, - name, }: Props) => { const theme = useTheme(); - const dropdownItemRef = useRef(null); const dropdownContainerRef = useRef(null); const [isOpen, setIsOpen] = useState(false); @@ -123,16 +92,6 @@ export const MatchColumnSelect = ({ setIsOpen(false); } - function renderIcon(icon: ReadonlyDeep) { - if (icon && React.isValidElement(icon)) { - return React.cloneElement(icon as any, { - size: 16, - color: theme.font.color.primary, - }); - } - return null; - } - useListenClickOutside({ refs: [dropdownContainerRef], callback: () => { @@ -146,28 +105,20 @@ export const MatchColumnSelect = ({ return ( <> - { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - dropdownItemRef.current = node; - refs.setReference(node); - }} - onClick={handleDropdownItemClick} - > - {renderIcon(value?.icon)} - - {value?.label ?? placeholder} - - - +
+ +
{isOpen && createPortal( {options?.map((option) => ( <> - handleChange(option)} disabled={ option.disabled && value?.value !== option.value } - > - {renderIcon(option?.icon)} - {option.label} - + LeftIcon={option?.icon} + text={option.label} + /> {option.disabled && value?.value !== option.value && createPortal( @@ -204,9 +153,7 @@ export const MatchColumnSelect = ({ )} ))} - {options?.length === 0 && ( - No result - )} + {options?.length === 0 && } , diff --git a/front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/TemplateColumn.tsx b/front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/TemplateColumn.tsx index 20181e46927..8c64b995bc3 100644 --- a/front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/TemplateColumn.tsx +++ b/front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/TemplateColumn.tsx @@ -109,7 +109,7 @@ export const TemplateColumn = ({ }); const selectOptions = [ { - icon: , + icon: IconForbid, value: 'do-not-import', label: 'Do not import', }, diff --git a/front/src/modules/spreadsheet-import/steps/components/ValidationStep/components/columns.tsx b/front/src/modules/spreadsheet-import/steps/components/ValidationStep/components/columns.tsx index b480d6fb398..8f11831eb9a 100644 --- a/front/src/modules/spreadsheet-import/steps/components/ValidationStep/components/columns.tsx +++ b/front/src/modules/spreadsheet-import/steps/components/ValidationStep/components/columns.tsx @@ -134,7 +134,7 @@ export const generateColumns = ( value={ value ? ({ - icon: null, + icon: undefined, ...value, } as const) : value diff --git a/front/src/modules/spreadsheet-import/tests/mockRsiValues.ts b/front/src/modules/spreadsheet-import/tests/mockRsiValues.ts index 6121e0bde13..a251903d250 100644 --- a/front/src/modules/spreadsheet-import/tests/mockRsiValues.ts +++ b/front/src/modules/spreadsheet-import/tests/mockRsiValues.ts @@ -1,5 +1,5 @@ import { defaultSpreadsheetImportProps } from '@/spreadsheet-import/provider/components/SpreadsheetImport'; -import type { SpreadsheetOptions } from '@/spreadsheet-import/types'; +import type { Fields, SpreadsheetOptions } from '@/spreadsheet-import/types'; const fields = [ { @@ -85,7 +85,7 @@ const fields = [ }, example: 'true', }, -] as const; +] as Fields; const mockComponentBehaviourForTypes = ( props: SpreadsheetOptions, diff --git a/front/src/modules/spreadsheet-import/types/index.ts b/front/src/modules/spreadsheet-import/types/index.ts index 5379384f550..ffacca7c2ed 100644 --- a/front/src/modules/spreadsheet-import/types/index.ts +++ b/front/src/modules/spreadsheet-import/types/index.ts @@ -3,6 +3,7 @@ import { ReadonlyDeep } from 'type-fest'; import { Columns } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep'; import { StepState } from '@/spreadsheet-import/steps/components/UploadFlow'; import { Meta } from '@/spreadsheet-import/steps/components/ValidationStep/types'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; export type SpreadsheetOptions = { // Is modal visible. @@ -65,7 +66,7 @@ export type Fields = ReadonlyDeep[]>; export type Field = { // Icon - icon: React.ReactNode; + icon: IconComponent | null | undefined; // UI-facing field label label: string; // Field's unique identifier @@ -96,7 +97,7 @@ export type Select = { export type SelectOption = { // Icon - icon?: React.ReactNode; + icon?: IconComponent | null; // UI-facing option label label: string; // Field entry matching criteria as well as select output diff --git a/front/src/modules/ui/board/components/BoardColumn.tsx b/front/src/modules/ui/board/components/BoardColumn.tsx index 0e6f2cb9bcc..bedf04b654d 100644 --- a/front/src/modules/ui/board/components/BoardColumn.tsx +++ b/front/src/modules/ui/board/components/BoardColumn.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from '@emotion/styled'; import { Tag } from '@/ui/tag/components/Tag'; +import { ThemeColor } from '@/ui/theme/constants/colors'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope'; @@ -52,7 +53,7 @@ const StyledNumChildren = styled.div` `; export type BoardColumnProps = { - color: string; + color?: ThemeColor; title: string; onDelete?: (id: string) => void; onTitleEdit: (title: string, color: string) => void; @@ -97,7 +98,7 @@ export function BoardColumn({ return ( - + {!!totalAmount && ${totalAmount}} {numChildren} @@ -107,7 +108,7 @@ export function BoardColumn({ onDelete={onDelete} onTitleEdit={onTitleEdit} title={title} - color={color} + color={color ?? 'gray'} stageId={stageId} /> )} diff --git a/front/src/modules/ui/board/components/BoardColumnEditTitleMenu.tsx b/front/src/modules/ui/board/components/BoardColumnEditTitleMenu.tsx index 7a67f37b53d..860234d902f 100644 --- a/front/src/modules/ui/board/components/BoardColumnEditTitleMenu.tsx +++ b/front/src/modules/ui/board/components/BoardColumnEditTitleMenu.tsx @@ -1,9 +1,10 @@ import { ChangeEvent, useState } from 'react'; import styled from '@emotion/styled'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; +import { MenuItemSelectColor } from '@/ui/menu-item/components/MenuItemSelectColor'; +import { ThemeColor } from '@/ui/theme/constants/colors'; import { textInputStyle } from '@/ui/theme/constants/effects'; import { debounce } from '~/utils/debounce'; @@ -32,21 +33,15 @@ export type BoardColumnEditTitleMenuProps = { onClose: () => void; title: string; onTitleEdit: (title: string, color: string) => void; - color: string; + color: ThemeColor; }; -const StyledColorSample = styled.div<{ colorName: string }>` - background-color: ${({ theme, colorName }) => - theme.tag.background[colorName]}; - border: 1px solid - ${({ theme, colorName }) => - theme.color[colorName as keyof typeof theme.color]}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - height: 12px; - width: 12px; -`; +type ColumnColorOption = { + name: string; + id: ThemeColor; +}; -export const COLOR_OPTIONS = [ +export const COLUMN_COLOR_OPTIONS: ColumnColorOption[] = [ { name: 'Green', id: 'green' }, { name: 'Turquoise', id: 'turquoise' }, { name: 'Sky', id: 'sky' }, @@ -85,18 +80,17 @@ export function BoardColumnEditTitleMenu({ /> - {COLOR_OPTIONS.map((colorOption) => ( - ( + { onTitleEdit(title, colorOption.id); onClose(); }} + color={colorOption.id} selected={colorOption.id === color} - > - - {colorOption.name} - + text={colorOption.name} + /> ))} ); diff --git a/front/src/modules/ui/board/components/BoardColumnMenu.tsx b/front/src/modules/ui/board/components/BoardColumnMenu.tsx index 6c23866b0f8..4ffcac1327c 100644 --- a/front/src/modules/ui/board/components/BoardColumnMenu.tsx +++ b/front/src/modules/ui/board/components/BoardColumnMenu.tsx @@ -5,7 +5,6 @@ import { Key } from 'ts-key-enum'; import { useCreateCompanyProgress } from '@/companies/hooks/useCreateCompanyProgress'; import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { IconPencil, IconPlus, IconTrash } from '@/ui/icon'; @@ -13,8 +12,9 @@ import { SingleEntitySelect } from '@/ui/input/relation-picker/components/Single import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; -import { icon } from '@/ui/theme/constants/icon'; +import { ThemeColor } from '@/ui/theme/constants/colors'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; @@ -32,7 +32,7 @@ const StyledMenuContainer = styled.div` `; type OwnProps = { - color: string; + color: ThemeColor; onClose: () => void; onDelete?: (id: string) => void; onTitleEdit: (title: string, color: string) => void; @@ -52,7 +52,7 @@ export function BoardColumnMenu({ }: OwnProps) { const [currentMenu, setCurrentMenu] = useState('actions'); - const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState); + const [, setBoardColumns] = useRecoilState(boardColumnsState); const boardColumnMenuRef = useRef(null); @@ -130,21 +130,21 @@ export function BoardColumnMenu({ {currentMenu === 'actions' && ( - setMenu('title')}> - - Rename - - setMenu('title')} + LeftIcon={IconPencil} + text="Rename" + /> + - - Delete - - setMenu('add')}> - - New opportunity - + LeftIcon={IconTrash} + text="Delete" + /> + setMenu('add')} + LeftIcon={IconPlus} + text="New opportunity" + /> )} {currentMenu === 'title' && ( diff --git a/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx b/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx index 77b02eae9b6..69bd1ae0a2c 100644 --- a/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx +++ b/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx @@ -7,18 +7,19 @@ import { v4 } from 'uuid'; import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; import { IconChevronLeft, - IconChevronRight, IconLayoutKanban, IconPlus, IconSettings, } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate'; +import { ThemeColor } from '@/ui/theme/constants/colors'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -35,16 +36,18 @@ const StyledIconSettings = styled(IconSettings)` margin-right: ${({ theme }) => theme.spacing(1)}; `; -const StyledIconChevronRight = styled(IconChevronRight)` - color: ${({ theme }) => theme.font.color.tertiary}; - margin-left: auto; -`; - enum BoardOptionsMenu { StageCreation = 'StageCreation', Stages = 'Stages', } +type ColumnForCreate = { + id: string; + colorCode: ThemeColor; + index: number; + title: string; +}; + export function BoardOptionsDropdownContent({ customHotkeyScope, onStageAdd, @@ -68,7 +71,7 @@ export function BoardOptionsDropdownContent({ ) return; - const columnToCreate = { + const columnToCreate: ColumnForCreate = { id: v4(), colorCode: 'gray', index: boardColumns.length, @@ -113,32 +116,26 @@ export function BoardOptionsDropdownContent({
- setCurrentMenu(BoardOptionsMenu.Stages)} - > - - Stages - - + LeftIcon={IconLayoutKanban} + text="Stages" + /> )} {currentMenu === BoardOptionsMenu.Stages && ( <> - } - onClick={resetMenu} - > + Stages - setCurrentMenu(BoardOptionsMenu.StageCreation)} - > - - Add stage - + LeftIcon={IconPlus} + text="Add stage" + /> )} diff --git a/front/src/modules/ui/board/components/__stories__/BoardColumnEditTitleMenu.stories.tsx b/front/src/modules/ui/board/components/__stories__/BoardColumnEditTitleMenu.stories.tsx index c26c6ca4f20..6f6190a52e3 100644 --- a/front/src/modules/ui/board/components/__stories__/BoardColumnEditTitleMenu.stories.tsx +++ b/front/src/modules/ui/board/components/__stories__/BoardColumnEditTitleMenu.stories.tsx @@ -4,7 +4,7 @@ import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { BoardColumnEditTitleMenu, - COLOR_OPTIONS, + COLUMN_COLOR_OPTIONS, } from '../BoardColumnEditTitleMenu'; const meta: Meta = { @@ -14,7 +14,7 @@ const meta: Meta = { argTypes: { color: { control: 'select', - options: COLOR_OPTIONS.map(({ id }) => id), + options: COLUMN_COLOR_OPTIONS.map(({ id }) => id), }, }, args: { color: 'green', title: 'Column title' }, diff --git a/front/src/modules/ui/board/hooks/useBoardContextMenuEntries.tsx b/front/src/modules/ui/board/hooks/useBoardContextMenuEntries.tsx index be0178f8e75..29fee01e0e8 100644 --- a/front/src/modules/ui/board/hooks/useBoardContextMenuEntries.tsx +++ b/front/src/modules/ui/board/hooks/useBoardContextMenuEntries.tsx @@ -16,7 +16,7 @@ export function useBoardContextMenuEntries() { setContextMenuEntries([ } + Icon={IconTrash} accent="danger" onClick={() => deleteSelectedBoardCards()} key="delete" diff --git a/front/src/modules/ui/board/types/BoardColumnDefinition.ts b/front/src/modules/ui/board/types/BoardColumnDefinition.ts index ce7be9befb2..ecb92214fbb 100644 --- a/front/src/modules/ui/board/types/BoardColumnDefinition.ts +++ b/front/src/modules/ui/board/types/BoardColumnDefinition.ts @@ -1,6 +1,8 @@ +import { ThemeColor } from '@/ui/theme/constants/colors'; + export type BoardColumnDefinition = { id: string; title: string; index: number; - colorCode: string; + colorCode?: ThemeColor; }; diff --git a/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx b/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx index c026219a0f8..1c07c11b118 100644 --- a/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx +++ b/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx @@ -1,32 +1,22 @@ -import { ReactNode } from 'react'; -import styled from '@emotion/styled'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; - -type ContextMenuEntryAccent = 'regular' | 'danger'; +type ContextMenuEntryAccent = 'default' | 'danger'; type OwnProps = { - icon: ReactNode; + Icon: IconComponent; label: string; accent?: ContextMenuEntryAccent; onClick: () => void; }; -const StyledButtonLabel = styled.div` - font-weight: ${({ theme }) => theme.font.weight.medium}; - margin-left: ${({ theme }) => theme.spacing(2)}; -`; - export function ContextMenuEntry({ label, - icon, - accent = 'regular', + Icon, + accent = 'default', onClick, }: OwnProps) { return ( - - {icon} - {label} - + ); } diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuCheckableItem.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuCheckableItem.tsx deleted file mode 100644 index bbd4928d074..00000000000 --- a/front/src/modules/ui/dropdown/components/DropdownMenuCheckableItem.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import styled from '@emotion/styled'; - -import { Checkbox } from '@/ui/input/checkbox/components/Checkbox'; - -import { DropdownMenuItem } from './DropdownMenuItem'; - -type Props = { - checked: boolean; - onChange?: (newCheckedValue: boolean) => void; - id?: string; -}; - -const StyledDropdownMenuCheckableItemContainer = styled(DropdownMenuItem)` - align-items: center; - display: flex; - justify-content: space-between; -`; - -const StyledLeftContainer = styled.div` - align-items: center; - display: flex; - - gap: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledChildrenContainer = styled.div` - align-items: center; - - display: flex; - font-size: ${({ theme }) => theme.font.size.sm}; - gap: ${({ theme }) => theme.spacing(2)}; -`; - -export function DropdownMenuCheckableItem({ - checked, - onChange, - children, -}: React.PropsWithChildren) { - function handleClick() { - onChange?.(!checked); - } - - return ( - - - - {children} - - - ); -} diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx index f3a7a49d3bf..d72c7f2a4b7 100644 --- a/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx +++ b/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx @@ -1,6 +1,9 @@ -import { ComponentProps, ReactElement } from 'react'; +import { ComponentProps } from 'react'; +import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; + const StyledHeader = styled.li` align-items: center; color: ${({ theme }) => theme.font.color.primary}; @@ -40,23 +43,31 @@ const StyledEndIconWrapper = styled(StyledStartIconWrapper)` `; type DropdownMenuHeaderProps = ComponentProps<'li'> & { - startIcon?: ReactElement; - endIcon?: ReactElement; + StartIcon?: IconComponent; + EndIcon?: IconComponent; }; export function DropdownMenuHeader({ children, - startIcon, - endIcon, + StartIcon, + EndIcon, ...props }: DropdownMenuHeaderProps) { + const theme = useTheme(); + return ( - {startIcon && ( - {startIcon} + {StartIcon && ( + + + )} {children} - {endIcon && {endIcon}} + {EndIcon && ( + + + + )} ); } diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx deleted file mode 100644 index f3fd76d42b2..00000000000 --- a/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ComponentProps } from 'react'; -import styled from '@emotion/styled'; -import { motion } from 'framer-motion'; - -import { FloatingIconButtonGroup } from '@/ui/button/components/FloatingIconButtonGroup'; -import { hoverBackground } from '@/ui/theme/constants/effects'; - -export type DropdownMenuItemAccent = 'regular' | 'danger'; - -const StyledItem = styled.li<{ accent: DropdownMenuItemAccent }>` - --horizontal-padding: ${({ theme }) => theme.spacing(1)}; - --vertical-padding: ${({ theme }) => theme.spacing(2)}; - - align-items: center; - - border-radius: ${({ theme }) => theme.border.radius.sm}; - color: ${({ theme, accent }) => - accent === 'danger' ? theme.color.red : theme.font.color.secondary}; - - cursor: pointer; - display: flex; - flex-direction: row; - - font-size: ${({ theme }) => theme.font.size.sm}; - - gap: ${({ theme }) => theme.spacing(2)}; - - height: calc(32px - 2 * var(--vertical-padding)); - - padding: var(--vertical-padding) var(--horizontal-padding); - - ${hoverBackground}; - - position: relative; - user-select: none; - - width: calc(100% - 2 * var(--horizontal-padding)); - - &:hover .actions-hover-container { - display: flex; - } -`; - -const StyledActions = styled(motion.div)` - display: none; - position: absolute; - right: ${({ theme }) => theme.spacing(1)}; -`; - -export type DropdownMenuItemProps = ComponentProps<'li'> & { - actions?: React.ReactNode[]; - accent?: DropdownMenuItemAccent; -}; - -export function DropdownMenuItem({ - actions, - children, - accent = 'regular', - ...props -}: DropdownMenuItemProps) { - return ( - - {children} - {actions && ( - - - {actions} - - - )} - - ); -} diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx deleted file mode 100644 index 639fff10b9c..00000000000 --- a/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useEffect } from 'react'; -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; - -import { IconCheck } from '@/ui/icon/index'; -import { hoverBackground } from '@/ui/theme/constants/effects'; - -import { DropdownMenuItem } from './DropdownMenuItem'; - -type Props = React.ComponentProps<'li'> & { - selected?: boolean; - hovered?: boolean; - disabled?: boolean; -}; - -const StyledDropdownMenuSelectableItemContainer = styled(DropdownMenuItem)< - Pick ->` - ${hoverBackground}; - - align-items: center; - - background: ${(props) => - props.hovered ? props.theme.background.transparent.light : 'transparent'}; - - display: flex; - justify-content: space-between; - - width: calc(100% - ${({ theme }) => theme.spacing(2)}); -`; - -const StyledLeftContainer = styled.div>` - align-items: center; - - display: flex; - - gap: ${({ theme }) => theme.spacing(2)}; - - opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}; - - overflow: hidden; -`; - -const StyledRightIcon = styled.div` - display: flex; -`; - -export function DropdownMenuSelectableItem({ - selected, - onClick, - children, - hovered, - disabled, - ...restProps -}: React.PropsWithChildren) { - const theme = useTheme(); - - function handleClick(event: React.MouseEvent) { - if (disabled) { - return; - } - - onClick?.(event); - } - - useEffect(() => { - if (hovered) { - window.scrollTo({ - behavior: 'smooth', - }); - } - }, [hovered]); - - return ( - - {children} - - {selected && } - - - ); -} diff --git a/front/src/modules/ui/dropdown/components/StyledDropdownMenuItemsContainer.tsx b/front/src/modules/ui/dropdown/components/StyledDropdownMenuItemsContainer.tsx index 0425bc1a9cc..9bc9b6e7c41 100644 --- a/front/src/modules/ui/dropdown/components/StyledDropdownMenuItemsContainer.tsx +++ b/front/src/modules/ui/dropdown/components/StyledDropdownMenuItemsContainer.tsx @@ -15,5 +15,6 @@ export const StyledDropdownMenuItemsContainer = styled.div<{ overflow-y: auto; padding: var(--padding); + padding-right: var(--padding); width: calc(100% - 2 * var(--padding)); `; diff --git a/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx b/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx index 0b27fef4188..4d7c805c7c6 100644 --- a/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx +++ b/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx @@ -2,17 +2,16 @@ import { useState } from 'react'; import styled from '@emotion/styled'; import type { Meta, StoryObj } from '@storybook/react'; -import { IconButton } from '@/ui/button/components/IconButton'; import { IconPlus, IconUser } from '@/ui/icon'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemMultiSelectAvatar } from '@/ui/menu-item/components/MenuItemMultiSelectAvatar'; +import { MenuItemSelectAvatar } from '@/ui/menu-item/components/MenuItemSelectAvatar'; import { Avatar } from '@/users/components/Avatar'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; -import { DropdownMenuCheckableItem } from '../DropdownMenuCheckableItem'; import { DropdownMenuHeader } from '../DropdownMenuHeader'; import { DropdownMenuInput } from '../DropdownMenuInput'; -import { DropdownMenuItem } from '../DropdownMenuItem'; -import { DropdownMenuSelectableItem } from '../DropdownMenuSelectableItem'; import { StyledDropdownMenu } from '../StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '../StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '../StyledDropdownMenuSeparator'; @@ -101,21 +100,22 @@ const FakeSelectableMenuItemList = ({ hasAvatar }: { hasAvatar?: boolean }) => { return ( <> {mockSelectArray.map((item) => ( - setSelectedItem(item.id)} - > - {hasAvatar && ( - - )} - {item.name} - + avatar={ + hasAvatar ? ( + + ) : undefined + } + text={item.name} + /> ))} ); @@ -127,28 +127,28 @@ const FakeCheckableMenuItemList = ({ hasAvatar }: { hasAvatar?: boolean }) => { return ( <> {mockSelectArray.map((item) => ( - { + selected={selectedItems.includes(item.id)} + onSelectChange={(checked) => { if (checked) { setSelectedItems([...selectedItems, item.id]); } else { setSelectedItems(selectedItems.filter((id) => id !== item.id)); } }} - > - {hasAvatar && ( - - )} - {item.name} - + avatar={ + hasAvatar ? ( + + ) : undefined + } + text={item.name} + /> ))} ); @@ -182,7 +182,7 @@ export const SimpleMenuItem: Story = { {mockSelectArray.map(({ name }) => ( - {name} + ))} @@ -198,14 +198,14 @@ export const WithHeaders: Story = { Subheader 1 {mockSelectArray.slice(0, 3).map(({ name }) => ( - {name} + ))} Subheader 2 {mockSelectArray.slice(3).map(({ name }) => ( - {name} + ))} @@ -218,10 +218,7 @@ export const WithIcons: Story = { {mockSelectArray.map(({ name }) => ( - - - {name} - + ))} @@ -234,15 +231,11 @@ export const WithActions: Story = { {mockSelectArray.map(({ name }, index) => ( - } />, - } />, - ]} - > - {name} - + iconButtons={[{ Icon: IconUser }, { Icon: IconPlus }]} + text={name} + /> ))} @@ -273,7 +266,7 @@ export const Search: Story = { {mockSelectArray.map(({ name }) => ( - {name} + ))} diff --git a/front/src/modules/ui/editable-field/components/EditableField.tsx b/front/src/modules/ui/editable-field/components/EditableField.tsx index c1eee60b3e3..682140f732f 100644 --- a/front/src/modules/ui/editable-field/components/EditableField.tsx +++ b/front/src/modules/ui/editable-field/components/EditableField.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import styled from '@emotion/styled'; import { motion } from 'framer-motion'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useEditableField } from '../hooks/useEditableField'; @@ -71,7 +72,7 @@ const StyledEditableFieldBaseContainer = styled.div` `; type OwnProps = { - iconLabel?: React.ReactNode; + IconLabel?: IconComponent; label?: string; labelFixedWidth?: number; useEditButton?: boolean; @@ -87,7 +88,7 @@ type OwnProps = { }; export function EditableField({ - iconLabel, + IconLabel, label, labelFixedWidth, useEditButton, @@ -125,7 +126,11 @@ export function EditableField({ onMouseLeave={handleContainerMouseLeave} > - {iconLabel && {iconLabel}} + {IconLabel && ( + + + + )} {label && ( {label} )} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableBooleanField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableBooleanField.tsx index 2c585ed4c33..0c19985ad63 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableBooleanField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableBooleanField.tsx @@ -18,7 +18,7 @@ export function GenericEditableBooleanField() { return ( } displayModeContentOnly /> diff --git a/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx index 65c698d3324..727a9faf0cd 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx @@ -32,7 +32,7 @@ export function GenericEditableDateField() { return ( } displayModeContent={} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx index 80cf8309147..4ac725cf2e5 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx @@ -31,7 +31,7 @@ export function GenericEditableNumberField() { return ( } displayModeContent={fieldValue} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/GenericEditablePhoneField.tsx b/front/src/modules/ui/editable-field/components/GenericEditablePhoneField.tsx index 5ca81dfdfa9..14cd9d00c16 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditablePhoneField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditablePhoneField.tsx @@ -33,7 +33,7 @@ export function GenericEditablePhoneField() { } displayModeContent={} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx index b566983b994..4f874ece12d 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx @@ -38,7 +38,7 @@ export function GenericEditableRelationField() { customEditHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker, }} - iconLabel={currentEditableFieldDefinition.icon} + IconLabel={currentEditableFieldDefinition.Icon} editModeContent={} displayModeContent={} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx index 2308f47b678..4927457820f 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx @@ -31,7 +31,7 @@ export function GenericEditableTextField() { return ( } displayModeContent={fieldValue} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx index 29d5392da01..b67e75d29dd 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx @@ -33,7 +33,7 @@ export function GenericEditableURLField() { } displayModeContent={} isDisplayModeContentEmpty={!fieldValue} diff --git a/front/src/modules/ui/editable-field/components/ProbabilityEditableField.tsx b/front/src/modules/ui/editable-field/components/ProbabilityEditableField.tsx index 83a5d341619..ba114063bb0 100644 --- a/front/src/modules/ui/editable-field/components/ProbabilityEditableField.tsx +++ b/front/src/modules/ui/editable-field/components/ProbabilityEditableField.tsx @@ -18,7 +18,7 @@ export function ProbabilityEditableField() { return ( } displayModeContentOnly disableHoverEffect diff --git a/front/src/modules/ui/editable-field/types/FieldDefinition.ts b/front/src/modules/ui/editable-field/types/FieldDefinition.ts index 82dd1306f54..4904b144057 100644 --- a/front/src/modules/ui/editable-field/types/FieldDefinition.ts +++ b/front/src/modules/ui/editable-field/types/FieldDefinition.ts @@ -1,9 +1,11 @@ +import { IconComponent } from '@/ui/icon/types/IconComponent'; + import { FieldMetadata, FieldType } from './FieldMetadata'; export type FieldDefinition = { key: string; name: string; - icon?: JSX.Element; + Icon?: IconComponent; type: FieldType; metadata: T; }; diff --git a/front/src/modules/ui/editable-field/types/ViewField.ts b/front/src/modules/ui/editable-field/types/ViewField.ts index 55288f2e607..8c29e830f1d 100644 --- a/front/src/modules/ui/editable-field/types/ViewField.ts +++ b/front/src/modules/ui/editable-field/types/ViewField.ts @@ -1,3 +1,4 @@ +import { IconComponent } from '@/ui/icon/types/IconComponent'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; @@ -118,7 +119,7 @@ export type ViewFieldMetadata = { type: ViewFieldType } & ( export type ViewFieldDefinition = { key: string; name: string; - icon?: JSX.Element; + Icon?: IconComponent; isVisible?: boolean; metadata: T; }; diff --git a/front/src/modules/ui/editable-field/variants/components/DateEditableField.tsx b/front/src/modules/ui/editable-field/variants/components/DateEditableField.tsx index 987fd8a0ada..3fe2d345519 100644 --- a/front/src/modules/ui/editable-field/variants/components/DateEditableField.tsx +++ b/front/src/modules/ui/editable-field/variants/components/DateEditableField.tsx @@ -1,5 +1,6 @@ import { EditableField } from '@/ui/editable-field/components/EditableField'; import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { parseDate } from '~/utils/date-utils'; @@ -7,13 +8,13 @@ import { parseDate } from '~/utils/date-utils'; import { EditableFieldEditModeDate } from './EditableFieldEditModeDate'; type OwnProps = { - icon?: React.ReactNode; + Icon?: IconComponent; label?: string; value: string | null | undefined; onSubmit?: (newValue: string) => void; }; -export function DateEditableField({ icon, value, label, onSubmit }: OwnProps) { +export function DateEditableField({ Icon, value, label, onSubmit }: OwnProps) { async function handleChange(newValue: string) { onSubmit?.(newValue); } @@ -25,7 +26,7 @@ export function DateEditableField({ icon, value, label, onSubmit }: OwnProps) { void; }; export function PhoneEditableField({ - icon, + Icon, placeholder, value, onSubmit, @@ -44,7 +45,7 @@ export function PhoneEditableField({ = { component: DateEditableField, decorators: [ComponentDecorator], argTypes: { - icon: { + Icon: { type: 'boolean', mapping: { - true: , + true: IconCalendar, false: undefined, }, }, @@ -21,7 +21,7 @@ const meta: Meta = { }, args: { value: new Date().toISOString(), - icon: true, + Icon: IconCalendar, }, }; diff --git a/front/src/modules/ui/editable-field/variants/components/__stories__/PhoneEditableField.stories.tsx b/front/src/modules/ui/editable-field/variants/components/__stories__/PhoneEditableField.stories.tsx index 1ee3f34bdff..78d09c094d5 100644 --- a/front/src/modules/ui/editable-field/variants/components/__stories__/PhoneEditableField.stories.tsx +++ b/front/src/modules/ui/editable-field/variants/components/__stories__/PhoneEditableField.stories.tsx @@ -10,17 +10,17 @@ const meta: Meta = { component: PhoneEditableField, decorators: [ComponentWithRouterDecorator], argTypes: { - icon: { + Icon: { type: 'boolean', mapping: { - true: , + true: IconPhone, false: undefined, }, }, }, args: { value: '+33714446494', - icon: true, + Icon: IconPhone, placeholder: 'Phone', }, }; diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx index 1b310875e21..5ae588685e9 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx @@ -1,8 +1,8 @@ import { Context } from 'react'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; @@ -43,8 +43,9 @@ export function FilterDropdownFilterSelect({ return ( {availableFilters.map((availableFilter, index) => ( - { setFilterDefinitionUsedInDropdown(availableFilter); @@ -58,10 +59,9 @@ export function FilterDropdownFilterSelect({ setFilterDropdownSearchInput(''); }} - > - {availableFilter.icon} - {availableFilter.label} - + LeftIcon={availableFilter.Icon} + text={availableFilter.label} + /> ))} ); diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx index 0c34ff520a9..0dde7dd27b7 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx @@ -1,5 +1,4 @@ import { Context } from 'react'; -import { useTheme } from '@emotion/react'; import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; import { IconChevronDown } from '@/ui/icon'; @@ -14,8 +13,6 @@ export function FilterDropdownOperandButton({ }: { context: Context; }) { - const theme = useTheme(); - const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, context, @@ -36,7 +33,7 @@ export function FilterDropdownOperandButton({ return ( } + EndIcon={IconChevronDown} onClick={() => setIsFilterDropdownOperandSelectUnfolded(true)} > {getOperandLabel(selectedOperandInDropdown)} diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandSelect.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandSelect.tsx index 3bb430e14db..115e83a9026 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandSelect.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandSelect.tsx @@ -1,7 +1,7 @@ import { Context } from 'react'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited'; @@ -66,14 +66,13 @@ export function FilterDropdownOperandSelect({ return ( {operandsForFilterType.map((filterOperand, index) => ( - { handleOperangeChange(filterOperand); }} - > - {getOperandLabel(filterOperand)} - + text={getOperandLabel(filterOperand)} + /> ))} ); diff --git a/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx b/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx index 01b66d1ff1e..61c6b9833b4 100644 --- a/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx +++ b/front/src/modules/ui/filter-n-sort/components/SortAndFilterBar.tsx @@ -158,14 +158,12 @@ function SortAndFilterBar({ return ( - ) : ( - - ) + Icon={ + sort.order === 'desc' + ? IconArrowNarrowDown + : IconArrowNarrowUp } isSort onRemove={() => onRemoveSort(sort.key)} @@ -181,12 +179,12 @@ function SortAndFilterBar({ return ( { removeFilter(filter.key); }} diff --git a/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx index db860dce935..ba878757ed8 100644 --- a/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx @@ -1,12 +1,10 @@ import { Context, useCallback, useState } from 'react'; -import { useTheme } from '@emotion/react'; import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; import { IconChevronDown } from '@/ui/icon'; -import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope'; import { SelectedSortType, SortType } from '../types/interface'; @@ -30,8 +28,6 @@ export function SortDropdownButton({ onSortSelect, HotkeyScope, }: OwnProps) { - const theme = useTheme(); - const [isUnfolded, setIsUnfolded] = useState(false); const [isOptionUnfolded, setIsOptionUnfolded] = useState(false); const [selectedSortDirection, setSelectedSortDirection] = @@ -74,21 +70,20 @@ export function SortDropdownButton({ {isOptionUnfolded ? ( {options.map((option, index) => ( - { setSelectedSortDirection(option); setIsOptionUnfolded(false); }} - > - {option === 'asc' ? 'Ascending' : 'Descending'} - + text={option === 'asc' ? 'Ascending' : 'Descending'} + /> ))} ) : ( <> } + EndIcon={IconChevronDown} onClick={() => setIsOptionUnfolded(true)} > {selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'} @@ -97,13 +92,13 @@ export function SortDropdownButton({ {availableSorts.map((sort, index) => ( - handleAddSort(sort)} - > - {sort.icon} - - + LeftIcon={sort.Icon} + text={sort.label} + /> ))} diff --git a/front/src/modules/ui/filter-n-sort/components/SortOrFilterChip.tsx b/front/src/modules/ui/filter-n-sort/components/SortOrFilterChip.tsx index aa72e88c6b9..80e2e4e85a8 100644 --- a/front/src/modules/ui/filter-n-sort/components/SortOrFilterChip.tsx +++ b/front/src/modules/ui/filter-n-sort/components/SortOrFilterChip.tsx @@ -1,16 +1,16 @@ -import { ReactNode } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconX } from '@/ui/icon/index'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; type OwnProps = { - id: string; labelKey?: string; labelValue: string; - icon: ReactNode; + Icon?: IconComponent; onRemove: () => void; isSort?: boolean; + testId?: string; }; type StyledChipProps = { @@ -55,20 +55,24 @@ const StyledLabelKey = styled.div` `; function SortOrFilterChip({ - id, labelKey, labelValue, - icon, + Icon, onRemove, isSort, + testId, }: OwnProps) { const theme = useTheme(); return ( - {icon} + {Icon && ( + + + + )} {labelKey && {labelKey}} {labelValue} - + diff --git a/front/src/modules/ui/filter-n-sort/types/FilterDefinition.ts b/front/src/modules/ui/filter-n-sort/types/FilterDefinition.ts index 34e2751fe14..b08ff8bda4a 100644 --- a/front/src/modules/ui/filter-n-sort/types/FilterDefinition.ts +++ b/front/src/modules/ui/filter-n-sort/types/FilterDefinition.ts @@ -1,9 +1,11 @@ +import { IconComponent } from '@/ui/icon/types/IconComponent'; + import { FilterType } from './FilterType'; export type FilterDefinition = { key: string; label: string; - icon: JSX.Element; + Icon: IconComponent; type: FilterType; entitySelectComponent?: JSX.Element; }; diff --git a/front/src/modules/ui/filter-n-sort/types/interface.ts b/front/src/modules/ui/filter-n-sort/types/interface.ts index 0cba99a2436..3bb4e43dd80 100644 --- a/front/src/modules/ui/filter-n-sort/types/interface.ts +++ b/front/src/modules/ui/filter-n-sort/types/interface.ts @@ -1,11 +1,10 @@ -import { ReactNode } from 'react'; - +import { IconComponent } from '@/ui/icon/types/IconComponent'; import { SortOrder as Order_By } from '~/generated/graphql'; export type SortType = { label: string; key: string; - icon?: ReactNode; + Icon?: IconComponent; orderByTemplate?: (order: Order_By) => OrderByTemplate[]; }; diff --git a/front/src/modules/ui/icon/types/IconComponent.ts b/front/src/modules/ui/icon/types/IconComponent.ts index 26c42b0260b..edcc48f5ddb 100644 --- a/front/src/modules/ui/icon/types/IconComponent.ts +++ b/front/src/modules/ui/icon/types/IconComponent.ts @@ -1,3 +1,3 @@ -import { ComponentType } from 'react'; +import { FunctionComponent } from 'react'; -export type IconComponent = ComponentType<{ size: number }>; +export type IconComponent = FunctionComponent<{ size?: number }>; diff --git a/front/src/modules/ui/input/relation-picker/components/MultipleEntitySelect.tsx b/front/src/modules/ui/input/relation-picker/components/MultipleEntitySelect.tsx index 943d6ee3aae..1cbe79a9a7b 100644 --- a/front/src/modules/ui/input/relation-picker/components/MultipleEntitySelect.tsx +++ b/front/src/modules/ui/input/relation-picker/components/MultipleEntitySelect.tsx @@ -1,12 +1,12 @@ import { useRef } from 'react'; import debounce from 'lodash.debounce'; -import { DropdownMenuCheckableItem } from '@/ui/dropdown/components/DropdownMenuCheckableItem'; import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemMultiSelectAvatar } from '@/ui/menu-item/components/MenuItemMultiSelectAvatar'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { Avatar } from '@/users/components/Avatar'; import { isNonEmptyString } from '~/utils/isNonEmptyString'; @@ -81,26 +81,25 @@ export function MultipleEntitySelect< {entitiesInDropdown?.map((entity) => ( - + selected={value[entity.id]} + onSelectChange={(newCheckedValue) => onChange({ ...value, [entity.id]: newCheckedValue }) } - > - - {entity.name} - + avatar={ + + } + text={entity.name} + /> ))} - {entitiesInDropdown?.length === 0 && ( - No result - )} + {entitiesInDropdown?.length === 0 && } ); diff --git a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx index 54e8e89670b..ece4d9a56a9 100644 --- a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx +++ b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelect.tsx @@ -1,12 +1,11 @@ import { useRef } from 'react'; -import { useTheme } from '@emotion/react'; import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; import { IconPlus } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { isDefined } from '~/utils/isDefined'; @@ -39,8 +38,6 @@ export function SingleEntitySelect< }) { const containerRef = useRef(null); - const theme = useTheme(); - const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch(); const showCreateButton = isDefined(onCreate) && searchFilter !== ''; @@ -76,10 +73,7 @@ export function SingleEntitySelect< {showCreateButton && ( <> - - - Add New - + diff --git a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx index bb8481c30ab..a91e10b06d2 100644 --- a/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx +++ b/front/src/modules/ui/input/relation-picker/components/SingleEntitySelectBase.tsx @@ -1,12 +1,10 @@ import { useRef } from 'react'; -import { useTheme } from '@emotion/react'; import { Key } from 'ts-key-enum'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; -import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { IconBuildingSkyscraper, IconUserCircle } from '@/ui/icon'; -import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemSelectAvatar } from '@/ui/menu-item/components/MenuItemSelectAvatar'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { Avatar } from '@/users/components/Avatar'; import { isDefined } from '~/utils/isDefined'; @@ -76,43 +74,44 @@ export function SingleEntitySelectBase< entitiesInDropdown = entitiesInDropdown.filter((entity) => isNonEmptyString(entity.name.trim()), ); - const theme = useTheme(); + + const NoUserIcon = + noUser?.entityType === Entity.User + ? IconUserCircle + : IconBuildingSkyscraper; return ( {noUser && ( - onEntitySelected(noUser)}> - {noUser.entityType === Entity.User ? ( - - ) : ( - - )} - {noUser.name} - + onEntitySelected(noUser)} + LeftIcon={NoUserIcon} + text={noUser.name} + /> )} {entities.loading ? ( ) : entitiesInDropdown.length === 0 ? ( - No result + ) : ( - entitiesInDropdown?.map((entity, index) => ( - ( + onEntitySelected(entity)} - > - - - + text={entity.name} + hovered={hoveredIndex === entitiesInDropdown.indexOf(entity)} + avatar={ + + } + /> )) )} diff --git a/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx index 648d050a686..2250632486f 100644 --- a/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx +++ b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx @@ -4,12 +4,12 @@ import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateAct import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity'; import { IconButton } from '@/ui/button/components/IconButton'; import { DropdownButton } from '@/ui/dropdown/components/DropdownButton'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; import { IconCheckbox, IconNotes, IconPlus } from '@/ui/icon/index'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { ActivityType } from '~/generated/graphql'; const StyledContainer = styled.div` @@ -50,20 +50,18 @@ export function ShowPageAddButton({ e.stopPropagation()} > - handleSelect(ActivityType.Note)} - accent="regular" - > - - Note - - handleSelect(ActivityType.Task)} - accent="regular" - > - - Task - + accent="default" + LeftIcon={IconNotes} + text="Note" + /> + handleSelect(ActivityType.Note)} + accent="default" + LeftIcon={IconCheckbox} + text="Task" + /> } diff --git a/front/src/modules/ui/menu-item/components/MenuItem.tsx b/front/src/modules/ui/menu-item/components/MenuItem.tsx index 4d701e3df98..0ad2e593fa4 100644 --- a/front/src/modules/ui/menu-item/components/MenuItem.tsx +++ b/front/src/modules/ui/menu-item/components/MenuItem.tsx @@ -1,3 +1,4 @@ +import { MouseEvent } from 'react'; import { useTheme } from '@emotion/react'; import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton'; @@ -10,24 +11,26 @@ import { MenuItemAccent } from '../types/MenuItemAccent'; export type MenuItemIconButton = { Icon: IconComponent; - onClick: () => void; + onClick?: (event: MouseEvent) => void; }; export type MenuItemProps = { - LeftIcon?: IconComponent; - accent: MenuItemAccent; + LeftIcon?: IconComponent | null; + accent?: MenuItemAccent; text: string; iconButtons?: MenuItemIconButton[]; - className: string; + className?: string; + testId?: string; onClick?: () => void; }; export function MenuItem({ LeftIcon, - accent, + accent = 'default', text, iconButtons, className, + testId, onClick, }: MenuItemProps) { const theme = useTheme(); @@ -35,8 +38,13 @@ export function MenuItem({ const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0; return ( - - + + {showIconButtons && ( {iconButtons?.map(({ Icon, onClick }, index) => ( diff --git a/front/src/modules/ui/menu-item/components/MenuItemMultiSelectAvatar.tsx b/front/src/modules/ui/menu-item/components/MenuItemMultiSelectAvatar.tsx new file mode 100644 index 00000000000..03f09e9b73b --- /dev/null +++ b/front/src/modules/ui/menu-item/components/MenuItemMultiSelectAvatar.tsx @@ -0,0 +1,51 @@ +import { ReactNode } from 'react'; +import styled from '@emotion/styled'; + +import { Checkbox } from '@/ui/input/checkbox/components/Checkbox'; + +import { + StyledMenuItemBase, + StyledMenuItemLabel, + StyledMenuItemLeftContent, +} from '../internals/components/StyledMenuItemBase'; + +const StyledLeftContentWithCheckboxContainer = styled.div` + align-items: center; + display: flex; + flex-direction: row; + gap: ${({ theme }) => theme.spacing(2)}; +`; + +type OwnProps = { + avatar?: ReactNode; + selected: boolean; + text: string; + className?: string; + onSelectChange?: (selected: boolean) => void; +}; + +export function MenuItemMultiSelectAvatar({ + avatar, + text, + selected, + className, + onSelectChange, +}: OwnProps) { + function handleOnClick() { + onSelectChange?.(!selected); + } + + return ( + + + + + {avatar} + + {text} + + + + + ); +} diff --git a/front/src/modules/ui/menu-item/components/MenuItemNavigate.tsx b/front/src/modules/ui/menu-item/components/MenuItemNavigate.tsx index 0ae19e95f0f..4fe76c0c502 100644 --- a/front/src/modules/ui/menu-item/components/MenuItemNavigate.tsx +++ b/front/src/modules/ui/menu-item/components/MenuItemNavigate.tsx @@ -10,7 +10,7 @@ export type MenuItemProps = { LeftIcon?: IconComponent; text: string; onClick?: () => void; - className: string; + className?: string; }; export function MenuItemNavigate({ diff --git a/front/src/modules/ui/menu-item/components/MenuItemSelect.tsx b/front/src/modules/ui/menu-item/components/MenuItemSelect.tsx index 6502e1438a6..623530e5a60 100644 --- a/front/src/modules/ui/menu-item/components/MenuItemSelect.tsx +++ b/front/src/modules/ui/menu-item/components/MenuItemSelect.tsx @@ -7,8 +7,12 @@ import { IconComponent } from '@/ui/icon/types/IconComponent'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; -const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ selected: boolean }>` - ${({ theme, selected }) => { +export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ + selected: boolean; + disabled?: boolean; + hovered?: boolean; +}>` + ${({ theme, selected, disabled, hovered }) => { if (selected) { return css` background: ${theme.background.transparent.light}; @@ -16,16 +20,33 @@ const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ selected: boolean }>` background: ${theme.background.transparent.medium}; } `; + } else if (disabled) { + return css` + background: inherit; + &:hover { + background: inherit; + } + + color: ${theme.font.color.tertiary}; + + cursor: default; + `; + } else if (hovered) { + return css` + background: ${theme.background.transparent.light}; + `; } }} `; type OwnProps = { - LeftIcon?: IconComponent; + LeftIcon: IconComponent | null | undefined; selected: boolean; text: string; - className: string; + className?: string; onClick?: () => void; + disabled?: boolean; + hovered?: boolean; }; export function MenuItemSelect({ @@ -34,6 +55,8 @@ export function MenuItemSelect({ selected, className, onClick, + disabled, + hovered, }: OwnProps) { const theme = useTheme(); @@ -42,6 +65,8 @@ export function MenuItemSelect({ onClick={onClick} className={className} selected={selected} + disabled={disabled} + hovered={hovered} > {selected && } diff --git a/front/src/modules/ui/menu-item/components/MenuItemSelectAvatar.tsx b/front/src/modules/ui/menu-item/components/MenuItemSelectAvatar.tsx new file mode 100644 index 00000000000..10f8afdd00e --- /dev/null +++ b/front/src/modules/ui/menu-item/components/MenuItemSelectAvatar.tsx @@ -0,0 +1,55 @@ +import { ReactNode } from 'react'; +import { useTheme } from '@emotion/react'; + +import { IconCheck } from '@/ui/icon'; +import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; + +import { + StyledMenuItemLabel, + StyledMenuItemLeftContent, +} from '../internals/components/StyledMenuItemBase'; + +import { StyledMenuItemSelect } from './MenuItemSelect'; + +type OwnProps = { + avatar: ReactNode; + selected: boolean; + text: string; + className?: string; + onClick?: () => void; + disabled?: boolean; + hovered?: boolean; + testId?: string; +}; + +export function MenuItemSelectAvatar({ + avatar, + text, + selected, + className, + onClick, + disabled, + hovered, + testId, +}: OwnProps) { + const theme = useTheme(); + + return ( + + + {avatar} + + + + + {selected && } + + ); +} diff --git a/front/src/modules/ui/menu-item/components/MenuItemSelectColor.tsx b/front/src/modules/ui/menu-item/components/MenuItemSelectColor.tsx new file mode 100644 index 00000000000..b2de9a27480 --- /dev/null +++ b/front/src/modules/ui/menu-item/components/MenuItemSelectColor.tsx @@ -0,0 +1,59 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; + +import { IconCheck } from '@/ui/icon'; +import { ThemeColor } from '@/ui/theme/constants/colors'; + +import { + StyledMenuItemLabel, + StyledMenuItemLeftContent, +} from '../internals/components/StyledMenuItemBase'; + +import { StyledMenuItemSelect } from './MenuItemSelect'; + +const StyledColorSample = styled.div<{ colorName: ThemeColor }>` + background-color: ${({ theme, colorName }) => + theme.tag.background[colorName]}; + border: 1px solid ${({ theme, colorName }) => theme.color[colorName]}; + border-radius: ${({ theme }) => theme.border.radius.sm}; + height: 12px; + width: 12px; +`; + +type OwnProps = { + selected: boolean; + text: string; + className?: string; + onClick?: () => void; + disabled?: boolean; + hovered?: boolean; + color: ThemeColor; +}; + +export function MenuItemSelectColor({ + color, + text, + selected, + className, + onClick, + disabled, + hovered, +}: OwnProps) { + const theme = useTheme(); + + return ( + + + + {text} + + {selected && } + + ); +} diff --git a/front/src/modules/ui/menu-item/internals/components/MenuItemLeftContent.tsx b/front/src/modules/ui/menu-item/internals/components/MenuItemLeftContent.tsx index 50358172969..b0462c58296 100644 --- a/front/src/modules/ui/menu-item/internals/components/MenuItemLeftContent.tsx +++ b/front/src/modules/ui/menu-item/internals/components/MenuItemLeftContent.tsx @@ -1,6 +1,7 @@ import { useTheme } from '@emotion/react'; import { IconComponent } from '@/ui/icon/types/IconComponent'; +import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; import { StyledMenuItemLabel, @@ -8,7 +9,7 @@ import { } from './StyledMenuItemBase'; type OwnProps = { - LeftIcon?: IconComponent; + LeftIcon: IconComponent | null | undefined; text: string; }; @@ -18,7 +19,9 @@ export function MenuItemLeftContent({ LeftIcon, text }: OwnProps) { return ( {LeftIcon && } - {text} + + + ); } diff --git a/front/src/modules/ui/menu-item/internals/components/StyledMenuItemBase.tsx b/front/src/modules/ui/menu-item/internals/components/StyledMenuItemBase.tsx index 73e8dbfa094..5620470b8ad 100644 --- a/front/src/modules/ui/menu-item/internals/components/StyledMenuItemBase.tsx +++ b/front/src/modules/ui/menu-item/internals/components/StyledMenuItemBase.tsx @@ -16,9 +16,9 @@ export const StyledMenuItemBase = styled.li` align-items: center; border-radius: ${({ theme }) => theme.border.radius.sm}; - cursor: pointer; display: flex; + flex-direction: row; font-size: ${({ theme }) => theme.font.size.sm}; @@ -29,8 +29,6 @@ export const StyledMenuItemBase = styled.li` justify-content: space-between; - padding: var(--vertical-padding) var(--horizontal-padding); - ${hoverBackground}; ${({ theme, accent }) => { @@ -43,6 +41,11 @@ export const StyledMenuItemBase = styled.li` } `; } + case 'placeholder': { + return css` + color: ${theme.font.color.tertiary}; + `; + } case 'default': default: { return css` @@ -52,7 +55,9 @@ export const StyledMenuItemBase = styled.li` } }} + padding: var(--vertical-padding) var(--horizontal-padding); position: relative; + user-select: none; width: calc(100% - 2 * var(--horizontal-padding)); diff --git a/front/src/modules/ui/menu-item/types/MenuItemAccent.ts b/front/src/modules/ui/menu-item/types/MenuItemAccent.ts index 72013f8a232..c67b7f0472c 100644 --- a/front/src/modules/ui/menu-item/types/MenuItemAccent.ts +++ b/front/src/modules/ui/menu-item/types/MenuItemAccent.ts @@ -1 +1 @@ -export type MenuItemAccent = 'default' | 'danger'; +export type MenuItemAccent = 'default' | 'danger' | 'placeholder'; diff --git a/front/src/modules/ui/table/components/ColumnHead.tsx b/front/src/modules/ui/table/components/ColumnHead.tsx index b0f15c4d75d..827102423f0 100644 --- a/front/src/modules/ui/table/components/ColumnHead.tsx +++ b/front/src/modules/ui/table/components/ColumnHead.tsx @@ -1,9 +1,11 @@ -import { ReactNode } from 'react'; +import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { IconComponent } from '@/ui/icon/types/IconComponent'; + type OwnProps = { viewName: string; - viewIcon?: ReactNode; + ViewIcon?: IconComponent; }; const StyledTitle = styled.div` @@ -32,10 +34,13 @@ const StyledText = styled.span` white-space: nowrap; `; -export function ColumnHead({ viewName, viewIcon }: OwnProps) { +export function ColumnHead({ viewName, ViewIcon }: OwnProps) { + const theme = useTheme(); return ( - {viewIcon} + + {ViewIcon && } + {viewName} ); diff --git a/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx b/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx index e763943dcdb..9d0a8fef067 100644 --- a/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx +++ b/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx @@ -1,13 +1,11 @@ -import { cloneElement, type ComponentProps, useCallback, useRef } from 'react'; -import { useTheme } from '@emotion/react'; +import { type ComponentProps, useCallback, useRef } from 'react'; import styled from '@emotion/styled'; -import { IconButton } from '@/ui/button/components/IconButton'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import type { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField'; import { IconPlus } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; @@ -31,7 +29,6 @@ export const EntityTableColumnMenu = ({ ...props }: EntityTableColumnMenuProps) => { const ref = useRef(null); - const theme = useTheme(); const hiddenTableColumns = useRecoilScopedValue( hiddenTableColumnsScopedSelector, @@ -57,22 +54,17 @@ export const EntityTableColumnMenu = ({ {hiddenTableColumns.map((column) => ( - } - onClick={() => handleAddColumn(column)} - />, + iconButtons={[ + { + Icon: IconPlus, + onClick: () => handleAddColumn(column), + }, ]} - > - {column.icon && - cloneElement(column.icon, { - size: theme.icon.size.md, - })} - {column.name} - + LeftIcon={column.Icon} + text={column.name} + /> ))} diff --git a/front/src/modules/ui/table/components/EntityTableHeader.tsx b/front/src/modules/ui/table/components/EntityTableHeader.tsx index feff50511a9..889559990b2 100644 --- a/front/src/modules/ui/table/components/EntityTableHeader.tsx +++ b/front/src/modules/ui/table/components/EntityTableHeader.tsx @@ -173,7 +173,7 @@ export function EntityTableHeader() { COLUMN_MIN_WIDTH, )} > - + void; @@ -59,8 +48,6 @@ export function TableOptionsDropdownContent({ onViewsChange, onImport, }: TableOptionsDropdownButtonProps) { - const theme = useTheme(); - const tableScopeId = useContextScopeId(TableRecoilScopeContext); const { closeDropdownButton } = useDropdownButton({ key: 'options' }); @@ -87,33 +74,6 @@ export function TableOptionsDropdownContent({ TableRecoilScopeContext, ); - const { handleColumnVisibilityChange } = useTableColumns(); - - const renderFieldActions = useCallback( - (column: ColumnDefinition) => - // Do not allow hiding last visible column - !column.isVisible || visibleTableColumns.length > 1 - ? [ - - ) : ( - - ) - } - onClick={() => handleColumnVisibilityChange(column)} - />, - ] - : undefined, - [ - handleColumnVisibilityChange, - theme.icon.size.sm, - visibleTableColumns.length, - ], - ); - const resetViewEditMode = useCallback(() => { setTableViewEditMode({ mode: undefined, viewId: undefined }); @@ -232,17 +192,17 @@ export function TableOptionsDropdownContent({ )} - handleSelectOption(Option.Properties)} - > - - Properties - + LeftIcon={IconTag} + text="Properties" + /> {onImport && ( - - - Import - + )} @@ -250,22 +210,20 @@ export function TableOptionsDropdownContent({ {selectedOption === Option.Properties && ( <> } + StartIcon={IconChevronLeft} onClick={resetSelectedOption} > Properties - {hiddenTableColumns.length > 0 && ( <> - diff --git a/front/src/modules/ui/table/options/components/TableOptionsDropdownSection.tsx b/front/src/modules/ui/table/options/components/TableOptionsDropdownSection.tsx index 36561193a4c..ed9095f480d 100644 --- a/front/src/modules/ui/table/options/components/TableOptionsDropdownSection.tsx +++ b/front/src/modules/ui/table/options/components/TableOptionsDropdownSection.tsx @@ -1,43 +1,39 @@ -import { cloneElement } from 'react'; -import { useTheme } from '@emotion/react'; - -import { - DropdownMenuItem, - DropdownMenuItemProps, -} from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader'; import type { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField'; +import { IconMinus, IconPlus } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { useTableColumns } from '../../hooks/useTableColumns'; import type { ColumnDefinition } from '../../types/ColumnDefinition'; -type TableOptionsDropdownSectionProps = { - renderActions: ( - column: ColumnDefinition, - ) => DropdownMenuItemProps['actions']; +type OwnProps = { title: string; columns: ColumnDefinition[]; }; -export function TableOptionsDropdownSection({ - renderActions, +export function TableOptionsDropdownColumnVisibility({ title, columns, -}: TableOptionsDropdownSectionProps) { - const theme = useTheme(); +}: OwnProps) { + const { handleColumnVisibilityChange } = useTableColumns(); return ( <> {title} {columns.map((column) => ( - - {column.icon && - cloneElement(column.icon, { - size: theme.icon.size.md, - })} - {column.name} - + handleColumnVisibilityChange(column), + }, + ]} + text={column.name} + /> ))} diff --git a/front/src/modules/ui/table/options/components/TableUpdateViewButtonGroup.tsx b/front/src/modules/ui/table/options/components/TableUpdateViewButtonGroup.tsx index 5c517f6e3a1..d0f5da945a6 100644 --- a/front/src/modules/ui/table/options/components/TableUpdateViewButtonGroup.tsx +++ b/front/src/modules/ui/table/options/components/TableUpdateViewButtonGroup.tsx @@ -1,12 +1,10 @@ import { useCallback, useState } from 'react'; -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { Button } from '@/ui/button/components/Button'; import { ButtonGroup } from '@/ui/button/components/ButtonGroup'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; import { DropdownMenuContainer } from '@/ui/filter-n-sort/components/DropdownMenuContainer'; @@ -17,6 +15,7 @@ import { canPersistFiltersScopedSelector } from '@/ui/filter-n-sort/states/selec import { canPersistSortsScopedSelector } from '@/ui/filter-n-sort/states/selectors/canPersistSortsScopedSelector'; import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState'; import { IconChevronDown, IconPlus } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; @@ -45,8 +44,6 @@ export const TableUpdateViewButtonGroup = ({ onViewSubmit, HotkeyScope, }: TableUpdateViewButtonGroupProps) => { - const theme = useTheme(); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); const tableScopeId = useContextScopeId(TableRecoilScopeContext); @@ -153,10 +150,11 @@ export const TableUpdateViewButtonGroup = ({ {isDropdownOpen && ( - - - Create view - + )} diff --git a/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx b/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx index 07db3d61563..615e5aecc57 100644 --- a/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx +++ b/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx @@ -3,8 +3,6 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useRecoilCallback, useSetRecoilState } from 'recoil'; -import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton'; -import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; @@ -20,6 +18,7 @@ import { IconPlus, IconTrash, } from '@/ui/icon'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { currentTableViewIdState, currentTableViewState, @@ -204,37 +203,35 @@ export const TableViewsDropdownButton = ({ > {tableViews.map((view) => ( - handleEditViewButtonClick(event, view.id)} - icon={} - />, - tableViews.length > 1 ? ( - - handleDeleteViewButtonClick(event, view.id) + iconButtons={[ + { + Icon: IconPencil, + onClick: (event: MouseEvent) => + handleEditViewButtonClick(event, view.id), + }, + tableViews.length > 1 + ? { + Icon: IconTrash, + onClick: (event: MouseEvent) => + handleDeleteViewButtonClick(event, view.id), } - icon={} - /> - ) : null, + : null, ].filter(assertNotNull)} onClick={() => handleViewSelect(view.id)} - > - - {view.name} - + LeftIcon={IconList} + text={view.name} + /> ))} - - - Add view - + ); diff --git a/front/src/modules/ui/tag/components/Tag.tsx b/front/src/modules/ui/tag/components/Tag.tsx index 95c508cbf75..3a95896bf5e 100644 --- a/front/src/modules/ui/tag/components/Tag.tsx +++ b/front/src/modules/ui/tag/components/Tag.tsx @@ -1,5 +1,7 @@ import styled from '@emotion/styled'; +import { ThemeColor } from '@/ui/theme/constants/colors'; + const tagColors = [ 'green', 'turquoise', @@ -40,7 +42,7 @@ const StyledTag = styled.h3<{ `; export type TagProps = { - color: string; + color: ThemeColor; text: string; onClick?: () => void; }; diff --git a/front/src/modules/ui/theme/constants/colors.ts b/front/src/modules/ui/theme/constants/colors.ts index 030026d18e0..f3261cd2319 100644 --- a/front/src/modules/ui/theme/constants/colors.ts +++ b/front/src/modules/ui/theme/constants/colors.ts @@ -22,8 +22,22 @@ export const grayScale = { gray0: '#ffffff', }; -export const color = { +export const mainColors = { yellow: '#ffd338', + green: '#55ef3c', + turquoise: '#15de8f', + sky: '#00e0ff', + blue: '#1961ed', + purple: '#915ffd', + pink: '#f54bd0', + red: '#f83e3e', + orange: '#ff7222', + gray: grayScale.gray30, +}; + +export type ThemeColor = keyof typeof mainColors; + +export const secondaryColors = { yellow80: '#2e2a1a', yellow70: '#453d1e', yellow60: '#746224', @@ -32,7 +46,7 @@ export const color = { yellow30: '#ffedaf', yellow20: '#fff6d7', yellow10: '#fffbeb', - green: '#55ef3c', + green80: '#1d2d1b', green70: '#23421e', green60: '#2a5822', @@ -41,7 +55,7 @@ export const color = { green30: '#ccfac5', green20: '#ddfcd8', green10: '#eefdec', - turquoise: '#15de8f', + turquoise80: '#172b23', turquoise70: '#173f2f', turquoise60: '#166747', @@ -50,7 +64,7 @@ export const color = { turquoise30: '#a1f2d2', turquoise20: '#d0f8e9', turquoise10: '#e8fcf4', - sky: '#00e0ff', + sky80: '#152b2e', sky70: '#123f45', sky60: '#0e6874', @@ -59,7 +73,7 @@ export const color = { sky30: '#99f3ff', sky20: '#ccf9ff', sky10: '#e5fcff', - blue: '#1961ed', + blue80: '#171e2c', blue70: '#172642', blue60: '#18356d', @@ -68,7 +82,7 @@ export const color = { blue30: '#a3c0f8', blue20: '#d1dffb', blue10: '#e8effd', - purple: '#915ffd', + purple80: '#231e2e', purple70: '#2f2545', purple60: '#483473', @@ -77,7 +91,7 @@ export const color = { purple30: '#d3bffe', purple20: '#e9dfff', purple10: '#f4efff', - pink: '#f54bd0', + pink80: '#2d1c29', pink70: '#43213c', pink60: '#702c61', @@ -86,7 +100,7 @@ export const color = { pink30: '#fbb7ec', pink20: '#fddbf6', pink10: '#feedfa', - red: '#f83e3e', + red80: '#2d1b1b', red70: '#441f1f', red60: '#712727', @@ -95,7 +109,7 @@ export const color = { red30: '#fcb2b2', red20: '#fed8d8', red10: '#feecec', - orange: '#ff7222', + orange80: '#2e2018', orange70: '#452919', orange60: '#743b1b', @@ -104,7 +118,7 @@ export const color = { orange30: '#ffc7a7', orange20: '#ffe3d3', orange10: '#fff1e9', - gray: grayScale.gray30, + gray80: grayScale.gray70, gray70: grayScale.gray65, gray60: grayScale.gray55, @@ -127,6 +141,11 @@ export const color = { blueAccent10: '#f5f9fd', }; +export const color = { + ...mainColors, + ...secondaryColors, +}; + export function rgba(hex: string, alpha: number) { const rgb = hexRgb(hex, { format: 'array' }).slice(0, -1).join(','); return `rgba(${rgb},${alpha})`; diff --git a/front/src/modules/ui/theme/utils/castStringAsThemeColor.ts b/front/src/modules/ui/theme/utils/castStringAsThemeColor.ts new file mode 100644 index 00000000000..b6e8aedce17 --- /dev/null +++ b/front/src/modules/ui/theme/utils/castStringAsThemeColor.ts @@ -0,0 +1,7 @@ +import { mainColors, ThemeColor } from '../constants/colors'; + +export const COLORS = Object.keys(mainColors); + +export function isThemeColor(color: string): color is ThemeColor { + return COLORS.includes(color); +} diff --git a/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx b/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx index 55737c39c96..5394bd7f904 100644 --- a/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx +++ b/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx @@ -35,11 +35,7 @@ export const FilterByName: Story = { const filterButton = await canvas.findByText('Filter'); await userEvent.click(filterButton); - const nameFilterButton = ( - await canvas.findAllByTestId('dropdown-menu-item') - ).find((item) => { - return item.textContent === 'Name'; - }); + const nameFilterButton = await canvas.findByTestId('select-filter-0'); assert(nameFilterButton); @@ -70,11 +66,9 @@ export const FilterByAccountOwner: Story = { const filterButton = await canvas.findByText('Filter'); await userEvent.click(filterButton); - const accountOwnerFilterButton = ( - await canvas.findAllByTestId('dropdown-menu-item') - ).find((item) => { - return item.textContent === 'Account owner'; - }); + const accountOwnerFilterButton = await canvas.findByTestId( + 'select-filter-5', + ); assert(accountOwnerFilterButton); @@ -89,11 +83,11 @@ export const FilterByAccountOwner: Story = { await sleep(1000); - const charlesChip = ( - await canvas.findAllByTestId('dropdown-menu-item') - ).find((item) => { - return item.textContent?.includes('Charles Test'); - }); + const charlesChip = (await canvas.findAllByTestId('menu-item')).find( + (item) => { + return item.textContent?.includes('Charles Test'); + }, + ); assert(charlesChip); diff --git a/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx b/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx index 3b0dbba2449..532c2b4c2e2 100644 --- a/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx +++ b/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx @@ -33,9 +33,8 @@ export const SortByName: Story = { const sortButton = await canvas.findByText('Sort'); await userEvent.click(sortButton); - const nameSortButton = canvas.getByText('Name', { - selector: 'li > div > div', - }); + const nameSortButton = await canvas.findByTestId('select-sort-0'); + await userEvent.click(nameSortButton); expect(await canvas.getByTestId('remove-icon-name')).toBeInTheDocument(); diff --git a/front/src/pages/companies/companies-filters.tsx b/front/src/pages/companies/companies-filters.tsx index 238aa0b12ff..de798927761 100644 --- a/front/src/pages/companies/companies-filters.tsx +++ b/front/src/pages/companies/companies-filters.tsx @@ -8,7 +8,6 @@ import { IconUsers, } from '@/ui/icon/index'; import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; -import { icon } from '@/ui/theme/constants/icon'; import { FilterDropdownUserSearchSelect } from '@/users/components/FilterDropdownUserSearchSelect'; import { Company } from '~/generated/graphql'; @@ -16,39 +15,37 @@ export const companiesFilters: FilterDefinitionByEntity[] = [ { key: 'name', label: 'Name', - icon: ( - - ), + Icon: IconBuildingSkyscraper, type: 'text', }, { key: 'employees', label: 'Employees', - icon: , + Icon: IconUsers, type: 'number', }, { key: 'domainName', label: 'URL', - icon: , + Icon: IconLink, type: 'text', }, { key: 'address', label: 'Address', - icon: , + Icon: IconMap, type: 'text', }, { key: 'createdAt', label: 'Created at', - icon: , + Icon: IconCalendarEvent, type: 'date', }, { key: 'accountOwnerId', label: 'Account owner', - icon: , + Icon: IconUser, type: 'entity', entitySelectComponent: ( diff --git a/front/src/pages/companies/companies-sorts.tsx b/front/src/pages/companies/companies-sorts.tsx index df264548376..9c76d8c5611 100644 --- a/front/src/pages/companies/companies-sorts.tsx +++ b/front/src/pages/companies/companies-sorts.tsx @@ -12,26 +12,26 @@ export const availableSorts: SortType[] = [ { key: 'name', label: 'Name', - icon: , + Icon: IconBuildingSkyscraper, }, { key: 'employees', label: 'Employees', - icon: , + Icon: IconUsers, }, { key: 'domainName', label: 'Url', - icon: , + Icon: IconLink, }, { key: 'address', label: 'Address', - icon: , + Icon: IconMap, }, { key: 'createdAt', label: 'Creation', - icon: , + Icon: IconCalendarEvent, }, ]; diff --git a/front/src/pages/companies/constants/companyShowFieldDefinition.tsx b/front/src/pages/companies/constants/companyShowFieldDefinition.tsx index 8c4d78a5277..ed4402da53b 100644 --- a/front/src/pages/companies/constants/companyShowFieldDefinition.tsx +++ b/front/src/pages/companies/constants/companyShowFieldDefinition.tsx @@ -23,7 +23,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'domainName', name: 'Domain name', - icon: , + Icon: IconLink, type: 'url', metadata: { fieldName: 'domainName', @@ -33,7 +33,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'accountOwner', name: 'Account owner', - icon: , + Icon: IconUserCircle, type: 'relation', metadata: { fieldName: 'accountOwner', @@ -43,7 +43,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'employees', name: 'Employees', - icon: , + Icon: IconUsers, type: 'number', metadata: { fieldName: 'employees', @@ -53,7 +53,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'address', name: 'Address', - icon: , + Icon: IconMap, type: 'text', metadata: { fieldName: 'address', @@ -63,7 +63,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'idealCustomerProfile', name: 'ICP', - icon: , + Icon: IconTarget, type: 'boolean', metadata: { fieldName: 'idealCustomerProfile', @@ -72,7 +72,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'xUrl', name: 'Twitter', - icon: , + Icon: IconBrandX, type: 'url', metadata: { fieldName: 'xUrl', @@ -82,7 +82,7 @@ export const companyShowFieldDefinition: FieldDefinition[] = [ { key: 'createdAt', name: 'Created at', - icon: , + Icon: IconCalendar, type: 'date', metadata: { fieldName: 'createdAt', diff --git a/front/src/pages/opportunities/opportunities-filters.tsx b/front/src/pages/opportunities/opportunities-filters.tsx index 17311c076b8..06749589a4b 100644 --- a/front/src/pages/opportunities/opportunities-filters.tsx +++ b/front/src/pages/opportunities/opportunities-filters.tsx @@ -7,7 +7,6 @@ import { IconCurrencyDollar, IconUser, } from '@/ui/icon/index'; -import { icon } from '@/ui/theme/constants/icon'; import { PipelineProgress } from '~/generated/graphql'; import { FilterDropdownPeopleSearchSelect } from '../../modules/people/components/FilterDropdownPeopleSearchSelect'; @@ -17,21 +16,19 @@ export const opportunitiesFilters: FilterDefinitionByEntity[] { key: 'amount', label: 'Amount', - icon: , + Icon: IconCurrencyDollar, type: 'number', }, { key: 'closeDate', label: 'Close date', - icon: , + Icon: IconCalendarEvent, type: 'date', }, { key: 'companyId', label: 'Company', - icon: ( - - ), + Icon: IconBuildingSkyscraper, type: 'entity', entitySelectComponent: ( [] { key: 'pointOfContactId', label: 'Point of contact', - icon: , + Icon: IconUser, type: 'entity', entitySelectComponent: ( , + Icon: IconCalendarEvent, }, { key: 'amount', label: 'Amount', - icon: , + Icon: IconCurrencyDollar, }, { key: 'closeDate', label: 'Expected close date', - icon: , + Icon: IconCalendarEvent, }, ] satisfies Array>; diff --git a/front/src/pages/people/__stories__/People.filterBy.stories.tsx b/front/src/pages/people/__stories__/People.filterBy.stories.tsx index 32be98a5d4e..e3db31dfe2d 100644 --- a/front/src/pages/people/__stories__/People.filterBy.stories.tsx +++ b/front/src/pages/people/__stories__/People.filterBy.stories.tsx @@ -35,11 +35,7 @@ export const Email: Story = { const filterButton = await canvas.findByText('Filter'); await userEvent.click(filterButton); - const emailFilterButton = ( - await canvas.findAllByTestId('dropdown-menu-item') - ).find((item) => { - return item.textContent?.includes('Email'); - }); + const emailFilterButton = await canvas.findByTestId('select-filter-2'); assert(emailFilterButton); @@ -70,11 +66,7 @@ export const CompanyName: Story = { const filterButton = await canvas.findByText('Filter'); await userEvent.click(filterButton); - const companyFilterButton = ( - await canvas.findAllByTestId('dropdown-menu-item') - ).find((item) => { - return item.textContent?.includes('Company'); - }); + const companyFilterButton = await canvas.findByTestId('select-filter-3'); assert(companyFilterButton); @@ -87,7 +79,7 @@ export const CompanyName: Story = { await sleep(500); - const qontoChip = (await canvas.findAllByTestId('dropdown-menu-item')).find( + const qontoChip = (await canvas.findAllByTestId('menu-item')).find( (item) => { return item.textContent?.includes('Qonto'); }, diff --git a/front/src/pages/people/__stories__/People.sortBy.stories.tsx b/front/src/pages/people/__stories__/People.sortBy.stories.tsx index d9f7b431084..902f5e0fdd2 100644 --- a/front/src/pages/people/__stories__/People.sortBy.stories.tsx +++ b/front/src/pages/people/__stories__/People.sortBy.stories.tsx @@ -34,9 +34,7 @@ export const Email: Story = { const sortButton = await canvas.findByText('Sort'); await userEvent.click(sortButton); - const emailSortButton = canvas.getByText('Email', { - selector: 'li > div > div', - }); + const emailSortButton = await canvas.findByTestId('select-sort-2'); await userEvent.click(emailSortButton); expect(await canvas.getByTestId('remove-icon-email')).toBeInTheDocument(); @@ -52,9 +50,7 @@ export const Cancel: Story = { const sortButton = await canvas.findByText('Sort'); await userEvent.click(sortButton); - const emailSortButton = canvas.getByText('Email', { - selector: 'li > div > div', - }); + const emailSortButton = await canvas.findByTestId('select-sort-2'); await userEvent.click(emailSortButton); expect(await canvas.getByTestId('remove-icon-email')).toBeInTheDocument(); diff --git a/front/src/pages/people/constants/personShowFieldDefinition.tsx b/front/src/pages/people/constants/personShowFieldDefinition.tsx index 2917a139bcc..63b94a69284 100644 --- a/front/src/pages/people/constants/personShowFieldDefinition.tsx +++ b/front/src/pages/people/constants/personShowFieldDefinition.tsx @@ -23,7 +23,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'email', name: 'Email', - icon: , + Icon: IconMail, type: 'text', metadata: { fieldName: 'email', @@ -33,7 +33,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'company', name: 'Company', - icon: , + Icon: IconBuildingSkyscraper, type: 'relation', metadata: { fieldName: 'company', @@ -44,7 +44,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'phone', name: 'Phone', - icon: , + Icon: IconPhone, type: 'phone', metadata: { fieldName: 'phone', @@ -54,7 +54,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'jobTitle', name: 'Job Title', - icon: , + Icon: IconBriefcase, type: 'text', metadata: { fieldName: 'jobTitle', @@ -64,7 +64,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'city', name: 'City', - icon: , + Icon: IconMap, type: 'text', metadata: { fieldName: 'city', @@ -74,7 +74,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'linkedinUrl', name: 'Linkedin URL', - icon: , + Icon: IconBrandLinkedin, type: 'url', metadata: { fieldName: 'linkedinUrl', @@ -84,7 +84,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'xUrl', name: 'X URL', - icon: , + Icon: IconBrandX, type: 'url', metadata: { fieldName: 'xUrl', @@ -94,7 +94,7 @@ export const personShowFieldDefinition: FieldDefinition[] = [ { key: 'createdAt', name: 'Created at', - icon: , + Icon: IconCalendar, type: 'date', metadata: { fieldName: 'createdAt', diff --git a/front/src/pages/people/people-filters.tsx b/front/src/pages/people/people-filters.tsx index 243802e3c87..e12e9b58bb7 100644 --- a/front/src/pages/people/people-filters.tsx +++ b/front/src/pages/people/people-filters.tsx @@ -9,34 +9,31 @@ import { IconUser, } from '@/ui/icon/index'; import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; -import { icon } from '@/ui/theme/constants/icon'; import { Person } from '~/generated/graphql'; export const peopleFilters: FilterDefinitionByEntity[] = [ { key: 'firstName', label: 'First name', - icon: , + Icon: IconUser, type: 'text', }, { key: 'lastName', label: 'Last name', - icon: , + Icon: IconUser, type: 'text', }, { key: 'email', label: 'Email', - icon: , + Icon: IconMail, type: 'text', }, { key: 'companyId', label: 'Company', - icon: ( - - ), + Icon: IconBuildingSkyscraper, type: 'entity', entitySelectComponent: ( @@ -45,19 +42,19 @@ export const peopleFilters: FilterDefinitionByEntity[] = [ { key: 'phone', label: 'Phone', - icon: , + Icon: IconPhone, type: 'text', }, { key: 'createdAt', label: 'Created at', - icon: , + Icon: IconCalendarEvent, type: 'date', }, { key: 'city', label: 'City', - icon: , + Icon: IconMap, type: 'text', }, ]; diff --git a/front/src/pages/people/people-sorts.tsx b/front/src/pages/people/people-sorts.tsx index 9ece4f20cb4..a195b208542 100644 --- a/front/src/pages/people/people-sorts.tsx +++ b/front/src/pages/people/people-sorts.tsx @@ -16,7 +16,7 @@ export const availableSorts: SortType[] = [ { key: 'fullname', label: 'People', - icon: , + Icon: IconUser, orderByTemplate: (order: Order_By) => [ { firstName: order }, @@ -26,28 +26,27 @@ export const availableSorts: SortType[] = [ { key: 'company_name', label: 'Company', - icon: , - + Icon: IconBuildingSkyscraper, orderByTemplate: (order: Order_By) => [{ company: { name: order } }], }, { key: 'email', label: 'Email', - icon: , + Icon: IconMail, }, { key: 'phone', label: 'Phone', - icon: , + Icon: IconPhone, }, { key: 'createdAt', label: 'Created at', - icon: , + Icon: IconCalendarEvent, }, { key: 'city', label: 'City', - icon: , + Icon: IconMap, }, ]; diff --git a/front/src/pages/tasks/tasks-filters.tsx b/front/src/pages/tasks/tasks-filters.tsx index 5f17c16f5a2..d7eb368352d 100644 --- a/front/src/pages/tasks/tasks-filters.tsx +++ b/front/src/pages/tasks/tasks-filters.tsx @@ -8,7 +8,7 @@ export const tasksFilters: FilterDefinitionByEntity[] = [ { key: 'assigneeId', label: 'Assignee', - icon: , + Icon: IconUser, type: 'entity', entitySelectComponent: (