diff --git a/docs/docs/developer/frontend/best-practices.mdx b/docs/docs/developer/frontend/best-practices.mdx
index 3e1d2cf814..c31df21dd3 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 d1372bacb6..2fe5df664e 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 5d01a62653..826893f8c6 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 a5d9f7e007..61ae4cb32a 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 8a55265114..a096d20e31 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 d75dab85ae..77d2d9d03d 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 26ddecb0d2..4ff2c92fc9 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 6ea68721a4..362d33af73 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 5cecff8273..7dbf90f288 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 5ec6348dfc..85002c8fb7 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 991d94e82f..2287a09186 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 ac053e7e81..fbb9e36ca7 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 8861b7a4c5..b98fa47eef 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 8862d552f2..b7341f86e1 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 2b16319574..53f0aec884 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 20f87db134..0e1cb67c47 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 db73c77179..81b73a0246 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 48a16a58f8..2d5365f76e 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 20181e4692..8c64b995bc 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 b480d6fb39..8f11831eb9 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 6121e0bde1..a251903d25 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 5379384f55..ffacca7c2e 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 0e6f2cb9bc..bedf04b654 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 7a67f37b53..860234d902 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 6c23866b0f..4ffcac1327 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"
+ />
+
- setMenu('add')}>
-
- New opportunity
-
+ LeftIcon={IconTrash}
+ text="Delete"
+ />
+
)}
{currentMenu === 'title' && (
diff --git a/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx b/front/src/modules/ui/board/components/BoardOptionsDropdownContent.tsx
index 77b02eae9b..69bd1ae0a2 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 c26c6ca4f2..6f6190a52e 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 be0178f8e7..29fee01e0e 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 ce7be9befb..ecb92214fb 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 c026219a0f..1c07c11b11 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 bbd4928d07..0000000000
--- 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 f3a7a49d3b..d72c7f2a4b 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 f3fd76d42b..0000000000
--- 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 639fff10b9..0000000000
--- 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 0425bc1a9c..9bc9b6e7c4 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 0b27fef418..4d7c805c7c 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 c1eee60b3e..682140f732 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 2c585ed4c3..0c19985ad6 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 65c698d332..727a9faf0c 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 80cf830914..4ac725cf2e 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 5ca81dfdfa..14cd9d00c1 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 b566983b99..4f874ece12 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 2308f47b67..4927457820 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 29d5392da0..b67e75d29d 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 83a5d34161..ba114063bb 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 82dd1306f5..4904b14405 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 55288f2e60..8c29e830f1 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 987fd8a0ad..3fe2d34551 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 1ee3f34bdf..78d09c094d 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 1b310875e2..5ae588685e 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 0c34ff520a..0dde7dd27b 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 3bb430e14d..115e83a902 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 01b66d1ff1..61c6b9833b 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 db860dce93..ba878757ed 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 aa72e88c6b..80e2e4e85a 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 34e2751fe1..b08ff8bda4 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 0cba99a243..3bb4e43dd8 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 26c42b0260..edcc48f5dd 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 943d6ee3aa..1cbe79a9a7 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 54e8e89670..ece4d9a56a 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 bb8481c30a..a91e10b06d 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}
-
+
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 648d050a68..2250632486 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 4d701e3df9..0ad2e593fa 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 0000000000..03f09e9b73
--- /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 0ae19e95f0..4fe76c0c50 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 6502e1438a..623530e5a6 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 0000000000..10f8afdd00
--- /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 0000000000..b2de9a2748
--- /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 5035817296..b0462c5829 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 73e8dbfa09..5620470b8a 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 72013f8a23..c67b7f0472 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 b0f15c4d75..827102423f 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 e763943dcd..9d0a8fef06 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 feff50511a..889559990b 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 36561193a4..ed9095f480 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 5c517f6e3a..d0f5da945a 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 07db3d6156..615e5aecc5 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 95c508cbf7..3a95896bf5 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 030026d18e..f3261cd231 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 0000000000..b6e8aedce1
--- /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 55737c39c9..5394bd7f90 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 3b0dbba244..532c2b4c2e 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 238aa0b12f..de79892776 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 df26454837..9c76d8c561 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 8c4d78a527..ed4402da53 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 17311c076b..06749589a4 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 32be98a5d4..e3db31dfe2 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 d9f7b43108..902f5e0fdd 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 2917a139bc..63b94a6928 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 243802e3c8..e12e9b58bb 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 9ece4f20cb..a195b20854 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 5f17c16f5a..d7eb368352 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: (