Chore(front): Create Storybook tests for the DropdownMenu component (#2157)
* Chore(front): Create Storybook tests for the DropdownMenu component Co-authored-by: Benjamin Mayanja V <vibenjamin6@gmail.com> Co-authored-by: FellipeMTX <fellipefacdir@gmail.com> * Fix the tests Co-authored-by: Benjamin Mayanja V <vibenjamin6@gmail.com> Co-authored-by: FellipeMTX <fellipefacdir@gmail.com> * Simplify Dropdown * Remove console.log --------- Co-authored-by: Benjamin Mayanja V <vibenjamin6@gmail.com> Co-authored-by: FellipeMTX <fellipefacdir@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
@ -22,7 +22,7 @@ export const ColumnHeadWithDropdown = ({
|
||||
}: ColumnHeadWithDropdownProps) => {
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={column.key + '-header'}>
|
||||
<DropdownMenu
|
||||
<Dropdown
|
||||
clickableComponent={<ColumnHead column={column} />}
|
||||
dropdownComponents={
|
||||
<DataTableColumnDropdownMenu
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { IconArrowLeft, IconArrowRight, IconEyeOff } from '@/ui/display/icon';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
|
||||
@ -52,7 +52,7 @@ export const DataTableColumnDropdownMenu = ({
|
||||
return column.key === primaryColumnKey ? (
|
||||
<></>
|
||||
) : (
|
||||
<StyledDropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuItemsContainer>
|
||||
{!isFirstColumn && (
|
||||
<MenuItem
|
||||
@ -74,6 +74,6 @@ export const DataTableColumnDropdownMenu = ({
|
||||
text="Hide"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,8 +3,8 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
@ -14,7 +14,7 @@ import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRe
|
||||
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
const StyledHeaderPlusButton = styled(StyledDropdownMenu)`
|
||||
const StyledHeaderPlusButton = styled(DropdownMenu)`
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
`;
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useResetRecoilState } from 'recoil';
|
||||
|
||||
import { ViewBarDropdownButton } from '@/ui/data/view-bar/components/ViewBarDropdownButton';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
@ -19,12 +20,14 @@ export const TableOptionsDropdown = ({
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
|
||||
return (
|
||||
<ViewBarDropdownButton
|
||||
buttonComponent={<TableOptionsDropdownButton />}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownId={TableOptionsDropdownId}
|
||||
dropdownComponents={<TableOptionsDropdownContent />}
|
||||
onClickOutside={resetViewEditMode}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={TableOptionsDropdownId}>
|
||||
<Dropdown
|
||||
clickableComponent={<TableOptionsDropdownButton />}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownOffset={{ y: 8 }}
|
||||
dropdownComponents={<TableOptionsDropdownContent />}
|
||||
onClickOutside={resetViewEditMode}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,10 +12,8 @@ import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuInputContainer } from '@/ui/layout/dropdown/components/DropdownMenuInputContainer';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
@ -128,28 +126,23 @@ export const TableOptionsDropdownContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu>
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInputContainer>
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={
|
||||
viewEditMode.mode === 'create' || !!viewEditMode.viewId
|
||||
}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
}
|
||||
/>
|
||||
</DropdownMenuInputContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuInput
|
||||
autoFocus={viewEditMode.mode === 'create' || !!viewEditMode.viewId}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => handleSelectMenu('fields')}
|
||||
@ -171,7 +164,7 @@ export const TableOptionsDropdownContent = () => {
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Fields
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleTableColumns}
|
||||
@ -181,7 +174,7 @@ export const TableOptionsDropdownContent = () => {
|
||||
/>
|
||||
{hiddenTableColumns.length > 0 && (
|
||||
<>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
fields={hiddenTableColumns}
|
||||
@ -192,6 +185,6 @@ export const TableOptionsDropdownContent = () => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StyledDropdownMenu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
@ -19,7 +19,7 @@ export const FilterDropdownEntitySelect = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<RecoilScope>
|
||||
{filterDefinitionUsedInDropdown.entitySelectComponent}
|
||||
</RecoilScope>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||
|
||||
import { MultipleFiltersButton } from './MultipleFiltersButton';
|
||||
import { MultipleFiltersDropdownContent } from './MultipleFiltersDropdownContent';
|
||||
import { ViewBarDropdownButton } from './ViewBarDropdownButton';
|
||||
|
||||
type MultipleFiltersDropdownButtonProps = {
|
||||
hotkeyScope: HotkeyScope;
|
||||
@ -14,11 +15,13 @@ export const MultipleFiltersDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: MultipleFiltersDropdownButtonProps) => {
|
||||
return (
|
||||
<ViewBarDropdownButton
|
||||
dropdownId={FilterDropdownId}
|
||||
buttonComponent={<MultipleFiltersButton />}
|
||||
dropdownComponents={<MultipleFiltersDropdownContent />}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={FilterDropdownId}>
|
||||
<Dropdown
|
||||
clickableComponent={<MultipleFiltersButton />}
|
||||
dropdownComponents={<MultipleFiltersDropdownContent />}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
dropdownOffset={{ y: 8 }}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
@ -35,36 +34,34 @@ export const MultipleFiltersDropdownContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu>
|
||||
<>
|
||||
{!filterDefinitionUsedInDropdown ? (
|
||||
<FilterDropdownFilterSelect />
|
||||
) : isFilterDropdownOperandSelectUnfolded ? (
|
||||
<FilterDropdownOperandSelect />
|
||||
) : (
|
||||
selectedOperandInDropdown && (
|
||||
<>
|
||||
<FilterDropdownOperandButton />
|
||||
<StyledDropdownMenuSeparator />
|
||||
{filterDefinitionUsedInDropdown.type === 'text' && (
|
||||
<FilterDropdownTextSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'number' && (
|
||||
<FilterDropdownNumberSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'date' && (
|
||||
<FilterDropdownDateSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySelect />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
</StyledDropdownMenu>
|
||||
<>
|
||||
{!filterDefinitionUsedInDropdown ? (
|
||||
<FilterDropdownFilterSelect />
|
||||
) : isFilterDropdownOperandSelectUnfolded ? (
|
||||
<FilterDropdownOperandSelect />
|
||||
) : (
|
||||
selectedOperandInDropdown && (
|
||||
<>
|
||||
<FilterDropdownOperandButton />
|
||||
<DropdownMenuSeparator />
|
||||
{filterDefinitionUsedInDropdown.type === 'text' && (
|
||||
<FilterDropdownTextSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'number' && (
|
||||
<FilterDropdownNumberSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'date' && (
|
||||
<FilterDropdownDateSearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySearchInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'entity' && (
|
||||
<FilterDropdownEntitySelect />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,8 +2,7 @@ import React from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import { IconChevronDown } from '@/ui/display/icon/index';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuContainer } from '@/ui/layout/dropdown/components/DropdownMenuContainer';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
@ -63,7 +62,7 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId="single-entity-filter-dropdown">
|
||||
<DropdownMenu
|
||||
<Dropdown
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
dropdownOffset={{ x: 0, y: -28 }}
|
||||
clickableComponent={
|
||||
@ -84,10 +83,10 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuContainer>
|
||||
<>
|
||||
<FilterDropdownEntitySearchInput />
|
||||
<FilterDropdownEntitySelect />
|
||||
</DropdownMenuContainer>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</DropdownScope>
|
||||
|
||||
@ -3,11 +3,12 @@ import { produce } from 'immer';
|
||||
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { LightButton } from '@/ui/input/button/components/LightButton';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
@ -19,8 +20,6 @@ import { sortsScopedState } from '../states/sortsScopedState';
|
||||
import { SortDefinition } from '../types/SortDefinition';
|
||||
import { SORT_DIRECTIONS, SortDirection } from '../types/SortDirection';
|
||||
|
||||
import { ViewBarDropdownButton } from './ViewBarDropdownButton';
|
||||
|
||||
export type SortDropdownButtonProps = {
|
||||
hotkeyScope: HotkeyScope;
|
||||
isPrimaryButton?: boolean;
|
||||
@ -91,56 +90,58 @@ export const SortDropdownButton = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewBarDropdownButton
|
||||
dropdownId={SortDropdownId}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
buttonComponent={
|
||||
<LightButton
|
||||
title="Sort"
|
||||
active={isSortSelected}
|
||||
onClick={handleButtonClick}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu>
|
||||
{isSortDirectionMenuUnfolded ? (
|
||||
<DropdownMenuItemsContainer>
|
||||
{SORT_DIRECTIONS.map((sortOrder, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setSelectedSortDirection(sortOrder);
|
||||
setIsSortDirectionMenuUnfolded(false);
|
||||
}}
|
||||
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuHeader
|
||||
EndIcon={IconChevronDown}
|
||||
onClick={() => setIsSortDirectionMenuUnfolded(true)}
|
||||
>
|
||||
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownScope dropdownScopeId={SortDropdownId}>
|
||||
<Dropdown
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
dropdownOffset={{ y: 8 }}
|
||||
clickableComponent={
|
||||
<LightButton
|
||||
title="Sort"
|
||||
active={isSortSelected}
|
||||
onClick={handleButtonClick}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<>
|
||||
{isSortDirectionMenuUnfolded ? (
|
||||
<DropdownMenuItemsContainer>
|
||||
{availableSorts.map((availableSort, index) => (
|
||||
{SORT_DIRECTIONS.map((sortOrder, index) => (
|
||||
<MenuItem
|
||||
testId={`select-sort-${index}`}
|
||||
key={index}
|
||||
onClick={() => handleAddSort(availableSort)}
|
||||
LeftIcon={availableSort.Icon}
|
||||
text={availableSort.label}
|
||||
onClick={() => {
|
||||
setSelectedSortDirection(sortOrder);
|
||||
setIsSortDirectionMenuUnfolded(false);
|
||||
}}
|
||||
text={sortOrder === 'asc' ? 'Ascending' : 'Descending'}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
</StyledDropdownMenu>
|
||||
}
|
||||
onClose={handleDropdownButtonClose}
|
||||
></ViewBarDropdownButton>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuHeader
|
||||
EndIcon={IconChevronDown}
|
||||
onClick={() => setIsSortDirectionMenuUnfolded(true)}
|
||||
>
|
||||
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
{availableSorts.map((availableSort, index) => (
|
||||
<MenuItem
|
||||
testId={`select-sort-${index}`}
|
||||
key={index}
|
||||
onClick={() => handleAddSort(availableSort)}
|
||||
LeftIcon={availableSort.Icon}
|
||||
text={availableSort.label}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
onClose={handleDropdownButtonClose}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -14,7 +14,6 @@ import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { IconChevronDown, IconPlus } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
|
||||
import { DropdownMenuContainer } from '@/ui/layout/dropdown/components/DropdownMenuContainer';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
@ -126,15 +125,13 @@ export const UpdateViewButtonGroup = ({
|
||||
</ButtonGroup>
|
||||
|
||||
{isDropdownOpen && (
|
||||
<DropdownMenuContainer onClose={handleDropdownClose}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleCreateViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Create view"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenuContainer>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleCreateViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Create view"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { Placement } from '@floating-ui/react';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
type ViewBarDropdownButtonProps = {
|
||||
buttonComponent: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
dropdownId: string;
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
};
|
||||
dropdownHotkeyScope: HotkeyScope;
|
||||
dropdownPlacement?: Placement;
|
||||
onClickOutside?: () => void;
|
||||
onClose?: () => void;
|
||||
onOpen?: () => void;
|
||||
};
|
||||
|
||||
export const ViewBarDropdownButton = ({
|
||||
buttonComponent,
|
||||
dropdownComponents,
|
||||
dropdownId,
|
||||
hotkey,
|
||||
dropdownHotkeyScope,
|
||||
dropdownPlacement = 'bottom-end',
|
||||
onClickOutside,
|
||||
onClose,
|
||||
onOpen,
|
||||
}: ViewBarDropdownButtonProps) => {
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={dropdownId}>
|
||||
<DropdownMenu
|
||||
clickableComponent={buttonComponent}
|
||||
dropdownComponents={dropdownComponents}
|
||||
hotkey={hotkey}
|
||||
dropdownHotkeyScope={dropdownHotkeyScope}
|
||||
dropdownOffset={{ x: 0, y: 8 }}
|
||||
dropdownPlacement={dropdownPlacement}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
@ -24,11 +24,12 @@ import {
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
} from '@/ui/display/icon';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
@ -41,8 +42,6 @@ import { ViewsDropdownId } from '../constants/ViewsDropdownId';
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveView } from '../hooks/useRemoveView';
|
||||
|
||||
import { ViewBarDropdownButton } from './ViewBarDropdownButton';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
`;
|
||||
@ -159,56 +158,57 @@ export const ViewsDropdownButton = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewBarDropdownButton
|
||||
dropdownId={ViewsDropdownId}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
buttonComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||
<StyledViewIcon size={theme.icon.size.md} />
|
||||
<StyledViewName>
|
||||
{currentView?.name || defaultViewName}
|
||||
</StyledViewName>
|
||||
<StyledDropdownLabelAdornments>
|
||||
· {entityCount} <IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledDropdownLabelAdornments>
|
||||
</StyledDropdownButtonContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu width={200}>
|
||||
<DropdownMenuItemsContainer>
|
||||
{views.map((view) => (
|
||||
<DropdownScope dropdownScopeId={ViewsDropdownId}>
|
||||
<Dropdown
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
clickableComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||
<StyledViewIcon size={theme.icon.size.md} />
|
||||
<StyledViewName>
|
||||
{currentView?.name || defaultViewName}
|
||||
</StyledViewName>
|
||||
<StyledDropdownLabelAdornments>
|
||||
· {entityCount} <IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledDropdownLabelAdornments>
|
||||
</StyledDropdownButtonContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
<>
|
||||
<DropdownMenuItemsContainer>
|
||||
{views.map((view) => (
|
||||
<MenuItem
|
||||
key={view.id}
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
},
|
||||
views.length > 1
|
||||
? {
|
||||
Icon: IconTrash,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleDeleteViewButtonClick(event, view.id),
|
||||
}
|
||||
: null,
|
||||
].filter(assertNotNull)}
|
||||
onClick={() => handleViewSelect(view.id)}
|
||||
LeftIcon={IconList}
|
||||
text={view.name}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownMenuSeparator />
|
||||
<StyledBoldDropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
key={view.id}
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
},
|
||||
views.length > 1
|
||||
? {
|
||||
Icon: IconTrash,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleDeleteViewButtonClick(event, view.id),
|
||||
}
|
||||
: null,
|
||||
].filter(assertNotNull)}
|
||||
onClick={() => handleViewSelect(view.id)}
|
||||
LeftIcon={IconList}
|
||||
text={view.name}
|
||||
onClick={handleAddViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add view"
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<StyledBoldDropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleAddViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add view"
|
||||
/>
|
||||
</StyledBoldDropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
}
|
||||
/>
|
||||
</StyledBoldDropdownMenuItemsContainer>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,11 +2,11 @@ import { useMemo, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
|
||||
@ -73,7 +73,7 @@ export const IconPicker = ({
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId="icon-picker">
|
||||
<DropdownMenu
|
||||
<Dropdown
|
||||
dropdownHotkeyScope={{ scope: IconPickerHotkeyScope.IconPicker }}
|
||||
clickableComponent={
|
||||
<IconButton
|
||||
@ -83,13 +83,13 @@ export const IconPicker = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu width={168}>
|
||||
<DropdownMenu width={168}>
|
||||
<DropdownMenuSearchInput
|
||||
placeholder="Search icon"
|
||||
autoFocus
|
||||
onChange={(event) => setSearchString(event.target.value)}
|
||||
/>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
{isLoading ? (
|
||||
<DropdownMenuSkeletonItem />
|
||||
@ -111,7 +111,7 @@ export const IconPicker = ({
|
||||
</StyledMenuIconItemsContainer>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={() => {
|
||||
@ -119,7 +119,7 @@ export const IconPicker = ({
|
||||
setSearchString('');
|
||||
}}
|
||||
onOpen={onOpen}
|
||||
></DropdownMenu>
|
||||
></Dropdown>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ import { CountryCallingCode } from 'libphonenumber-js';
|
||||
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { IconWorld } from '@/ui/input/constants/icons';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
|
||||
@ -123,7 +123,7 @@ export const CountryPickerDropdownButton = ({
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId="country-picker">
|
||||
<DropdownMenu
|
||||
<Dropdown
|
||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
||||
clickableComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
|
||||
|
||||
@ -58,13 +58,13 @@ export const CountryPickerDropdownSelect = ({
|
||||
return (
|
||||
<>
|
||||
<StyledDropdownMenuContainer data-select-disable>
|
||||
<StyledDropdownMenu width="240px" disableBlur>
|
||||
<DropdownMenu width="240px" disableBlur>
|
||||
<DropdownMenuSearchInput
|
||||
value={searchFilter}
|
||||
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{filteredCountries?.length === 0 ? (
|
||||
<MenuItem text="No result" />
|
||||
@ -102,7 +102,7 @@ export const CountryPickerDropdownSelect = ({
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
</StyledDropdownMenuContainer>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { useRef } from 'react';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
@ -72,13 +72,13 @@ export const MultipleEntitySelect = <
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu ref={containerRef} data-select-disable>
|
||||
<DropdownMenu ref={containerRef} data-select-disable>
|
||||
<DropdownMenuSearchInput
|
||||
value={searchFilter}
|
||||
onChange={handleFilterChange}
|
||||
autoFocus
|
||||
/>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{entitiesInDropdown?.map((entity) => (
|
||||
<MenuItemMultiSelectAvatar
|
||||
@ -101,6 +101,6 @@ export const MultipleEntitySelect = <
|
||||
))}
|
||||
{entitiesInDropdown?.length === 0 && <MenuItem text="No result" />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
@ -61,7 +61,7 @@ export const SingleEntitySelect = <
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu
|
||||
<DropdownMenu
|
||||
disableBlur={disableBackgroundBlur}
|
||||
ref={containerRef}
|
||||
width={width}
|
||||
@ -72,7 +72,7 @@ export const SingleEntitySelect = <
|
||||
onChange={handleSearchFilterChange}
|
||||
autoFocus
|
||||
/>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<SingleEntitySelectBase
|
||||
{...{
|
||||
EmptyIcon,
|
||||
@ -86,6 +86,6 @@ export const SingleEntitySelect = <
|
||||
showCreateButton,
|
||||
}}
|
||||
/>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import { Key } from 'ts-key-enum';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
|
||||
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
|
||||
@ -155,7 +155,7 @@ export const SingleEntitySelectBase = <
|
||||
{showCreateButton && (
|
||||
<>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<CreateNewButton
|
||||
onClick={onCreate}
|
||||
LeftIcon={IconPlus}
|
||||
|
||||
@ -4,7 +4,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
@ -123,7 +123,7 @@ export const BoardColumnEditTitleMenu = ({
|
||||
autoFocus
|
||||
/>
|
||||
</StyledEditTitleContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
{COLUMN_COLOR_OPTIONS.map((colorOption) => (
|
||||
<MenuItemSelectColor
|
||||
key={colorOption.name}
|
||||
@ -135,7 +135,7 @@ export const BoardColumnEditTitleMenu = ({
|
||||
text={colorOption.name}
|
||||
/>
|
||||
))}
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<MenuItem
|
||||
onClick={handleDelete}
|
||||
LeftIcon={IconTrash}
|
||||
|
||||
@ -15,8 +15,8 @@ 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 { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
@ -138,7 +138,7 @@ export const BoardColumnMenu = ({
|
||||
|
||||
return (
|
||||
<StyledMenuContainer ref={boardColumnMenuRef}>
|
||||
<StyledDropdownMenu data-select-disable>
|
||||
<DropdownMenu data-select-disable>
|
||||
{currentMenu === 'actions' && (
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
@ -183,7 +183,7 @@ export const BoardColumnMenu = ({
|
||||
selectedEntity={companies.selectedEntities[0]}
|
||||
/>
|
||||
)}
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
</StyledMenuContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { useResetRecoilState } from 'recoil';
|
||||
|
||||
import { ViewBarDropdownButton } from '@/ui/data/view-bar/components/ViewBarDropdownButton';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
|
||||
import { Dropdown } from '../../dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '../../dropdown/scopes/DropdownScope';
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton';
|
||||
@ -23,17 +24,18 @@ export const BoardOptionsDropdown = ({
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
|
||||
return (
|
||||
<ViewBarDropdownButton
|
||||
buttonComponent={<BoardOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<BoardOptionsDropdownContent
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownId={BoardScopeIds.OptionsDropdown}
|
||||
onClickOutside={resetViewEditMode}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={BoardScopeIds.OptionsDropdown}>
|
||||
<Dropdown
|
||||
clickableComponent={<BoardOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<BoardOptionsDropdownContent
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
onClickOutside={resetViewEditMode}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -22,11 +22,9 @@ import {
|
||||
} from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuInputContainer } from '@/ui/layout/dropdown/components/DropdownMenuInputContainer';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
|
||||
@ -166,28 +164,24 @@ export const BoardOptionsDropdownContent = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu>
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInputContainer>
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={
|
||||
viewEditMode.mode === 'create' || !!viewEditMode.viewId
|
||||
}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
}
|
||||
/>
|
||||
</DropdownMenuInputContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode.mode === 'create' || !!viewEditMode.viewId}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
onClick={() => handleMenuNavigate('fields')}
|
||||
@ -207,7 +201,7 @@ export const BoardOptionsDropdownContent = ({
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Stages
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => setCurrentMenu('stage-creation')}
|
||||
@ -229,7 +223,7 @@ export const BoardOptionsDropdownContent = ({
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Fields
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
{hasVisibleFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
@ -238,9 +232,7 @@ export const BoardOptionsDropdownContent = ({
|
||||
isDraggable={true}
|
||||
/>
|
||||
)}
|
||||
{hasVisibleFields && hasHiddenFields && (
|
||||
<StyledDropdownMenuSeparator />
|
||||
)}
|
||||
{hasVisibleFields && hasHiddenFields && <DropdownMenuSeparator />}
|
||||
{hasHiddenFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
@ -251,6 +243,6 @@ export const BoardOptionsDropdownContent = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StyledDropdownMenu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
117
front/src/modules/ui/layout/dropdown/components/Dropdown.tsx
Normal file
117
front/src/modules/ui/layout/dropdown/components/Dropdown.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import { useRef } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { flip, offset, Placement, useFloating } from '@floating-ui/react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
import { useDropdown } from '../hooks/useDropdown';
|
||||
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||
|
||||
import { DropdownMenu } from './DropdownMenu';
|
||||
import { DropdownToggleEffect } from './DropdownToggleEffect';
|
||||
|
||||
type DropdownProps = {
|
||||
clickableComponent?: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
};
|
||||
dropdownHotkeyScope: HotkeyScope;
|
||||
dropdownPlacement?: Placement;
|
||||
dropdownMenuWidth?: number;
|
||||
dropdownOffset?: { x?: number; y?: number };
|
||||
onClickOutside?: () => void;
|
||||
onClose?: () => void;
|
||||
onOpen?: () => void;
|
||||
};
|
||||
|
||||
export const Dropdown = ({
|
||||
clickableComponent,
|
||||
dropdownComponents,
|
||||
dropdownMenuWidth = 160,
|
||||
hotkey,
|
||||
dropdownHotkeyScope,
|
||||
dropdownPlacement = 'bottom-end',
|
||||
dropdownOffset = { x: 0, y: 0 },
|
||||
onClickOutside,
|
||||
onClose,
|
||||
onOpen,
|
||||
}: DropdownProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown();
|
||||
|
||||
const offsetMiddlewares = [];
|
||||
if (dropdownOffset.x) {
|
||||
offsetMiddlewares.push(offset({ crossAxis: dropdownOffset.x }));
|
||||
}
|
||||
|
||||
if (dropdownOffset.y) {
|
||||
offsetMiddlewares.push(offset({ mainAxis: dropdownOffset.y }));
|
||||
}
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: dropdownPlacement,
|
||||
middleware: [flip(), ...offsetMiddlewares],
|
||||
});
|
||||
|
||||
const handleHotkeyTriggered = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [containerRef],
|
||||
callback: () => {
|
||||
onClickOutside?.();
|
||||
|
||||
if (isDropdownOpen) {
|
||||
closeDropdown();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useInternalHotkeyScopeManagement({
|
||||
dropdownHotkeyScopeFromParent: dropdownHotkeyScope,
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
closeDropdown();
|
||||
},
|
||||
dropdownHotkeyScope.scope,
|
||||
[closeDropdown],
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
{clickableComponent && (
|
||||
<div ref={refs.setReference} onClick={toggleDropdown}>
|
||||
{clickableComponent}
|
||||
</div>
|
||||
)}
|
||||
{hotkey && (
|
||||
<HotkeyEffect
|
||||
hotkey={hotkey}
|
||||
onHotkeyTriggered={handleHotkeyTriggered}
|
||||
/>
|
||||
)}
|
||||
{isDropdownOpen && (
|
||||
<DropdownMenu
|
||||
width={dropdownMenuWidth}
|
||||
data-select-disable
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
<DropdownToggleEffect onDropdownClose={onClose} onDropdownOpen={onOpen} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,103 +1,25 @@
|
||||
import { useRef } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { flip, offset, Placement, useFloating } from '@floating-ui/react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
const StyledDropdownMenu = styled.div<{
|
||||
disableBlur?: boolean;
|
||||
width?: `${string}px` | 'auto' | number;
|
||||
}>`
|
||||
backdrop-filter: ${({ disableBlur }) =>
|
||||
disableBlur ? 'none' : 'blur(20px)'};
|
||||
|
||||
import { useDropdown } from '../hooks/useDropdown';
|
||||
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
|
||||
import { DropdownToggleEffect } from './DropdownToggleEffect';
|
||||
display: flex;
|
||||
|
||||
type DropdownMenuProps = {
|
||||
clickableComponent?: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
};
|
||||
dropdownHotkeyScope: HotkeyScope;
|
||||
dropdownPlacement?: Placement;
|
||||
dropdownOffset?: { x: number; y: number };
|
||||
onClickOutside?: () => void;
|
||||
onClose?: () => void;
|
||||
onOpen?: () => void;
|
||||
};
|
||||
flex-direction: column;
|
||||
|
||||
export const DropdownMenu = ({
|
||||
clickableComponent,
|
||||
dropdownComponents,
|
||||
hotkey,
|
||||
dropdownHotkeyScope,
|
||||
dropdownPlacement = 'bottom-end',
|
||||
dropdownOffset = { x: 0, y: 0 },
|
||||
onClickOutside,
|
||||
onClose,
|
||||
onOpen,
|
||||
}: DropdownMenuProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
overflow: hidden;
|
||||
|
||||
const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown();
|
||||
width: ${({ width }) =>
|
||||
width ? `${typeof width === 'number' ? `${width}px` : width}` : '160px'};
|
||||
`;
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: dropdownPlacement,
|
||||
middleware: [
|
||||
flip(),
|
||||
offset({ mainAxis: dropdownOffset.y, crossAxis: dropdownOffset.x }),
|
||||
],
|
||||
});
|
||||
|
||||
const handleHotkeyTriggered = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [containerRef],
|
||||
callback: () => {
|
||||
onClickOutside?.();
|
||||
|
||||
if (isDropdownOpen) {
|
||||
closeDropdown();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useInternalHotkeyScopeManagement({
|
||||
dropdownHotkeyScopeFromParent: dropdownHotkeyScope,
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
closeDropdown();
|
||||
},
|
||||
dropdownHotkeyScope.scope,
|
||||
[closeDropdown],
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
{clickableComponent && (
|
||||
<div ref={refs.setReference} onClick={toggleDropdown}>
|
||||
{clickableComponent}
|
||||
</div>
|
||||
)}
|
||||
{hotkey && (
|
||||
<HotkeyEffect
|
||||
hotkey={hotkey}
|
||||
onHotkeyTriggered={handleHotkeyTriggered}
|
||||
/>
|
||||
)}
|
||||
{isDropdownOpen && (
|
||||
<div data-select-disable ref={refs.setFloating} style={floatingStyles}>
|
||||
{dropdownComponents}
|
||||
</div>
|
||||
)}
|
||||
<DropdownToggleEffect onDropdownClose={onClose} onDropdownOpen={onOpen} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export const DropdownMenu = StyledDropdownMenu;
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { HTMLAttributes, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
const StyledDropdownMenuContainer = styled.ul<{
|
||||
anchor: 'left' | 'right';
|
||||
}>`
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
${({ anchor }) => {
|
||||
if (anchor === 'right') return 'right: 0';
|
||||
}};
|
||||
top: 14px;
|
||||
`;
|
||||
|
||||
export type DropdownMenuContainerProps = {
|
||||
anchor?: 'left' | 'right';
|
||||
children: React.ReactNode;
|
||||
onClose?: () => void;
|
||||
width?: `${string}px` | 'auto' | number;
|
||||
} & HTMLAttributes<HTMLUListElement>;
|
||||
|
||||
export const DropdownMenuContainer = ({
|
||||
anchor = 'right',
|
||||
children,
|
||||
onClose,
|
||||
width,
|
||||
}: DropdownMenuContainerProps) => {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [dropdownRef],
|
||||
callback: () => {
|
||||
onClose?.();
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledDropdownMenuContainer data-select-disable anchor={anchor}>
|
||||
<StyledDropdownMenu ref={dropdownRef} width={width}>
|
||||
{children}
|
||||
</StyledDropdownMenu>
|
||||
</StyledDropdownMenuContainer>
|
||||
);
|
||||
};
|
||||
@ -1,9 +1,10 @@
|
||||
import { forwardRef, InputHTMLAttributes } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { rgba } from '@/ui/theme/constants/colors';
|
||||
import { textInputStyle } from '@/ui/theme/constants/effects';
|
||||
|
||||
const StyledViewNameInput = styled.input`
|
||||
const StyledInput = styled.input`
|
||||
${textInputStyle}
|
||||
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
@ -20,4 +21,24 @@ const StyledViewNameInput = styled.input`
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledViewNameInput as DropdownMenuInput };
|
||||
const StyledInputContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const DropdownMenuInput = forwardRef<
|
||||
HTMLInputElement,
|
||||
InputHTMLAttributes<HTMLInputElement>
|
||||
>(({ autoFocus, defaultValue, placeholder }, ref) => {
|
||||
return (
|
||||
<StyledInputContainer>
|
||||
<StyledInput
|
||||
autoFocus={autoFocus}
|
||||
defaultValue={defaultValue}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export { StyledInputContainer as DropdownMenuInputContainer };
|
||||
@ -1,8 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const StyledDropdownMenuSeparator = styled.div`
|
||||
const StyledDropdownMenuSeparator = styled.div`
|
||||
background-color: ${({ theme }) => theme.border.color.light};
|
||||
height: 1px;
|
||||
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const DropdownMenuSeparator = StyledDropdownMenuSeparator;
|
||||
@ -1,23 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const StyledDropdownMenu = styled.div<{
|
||||
disableBlur?: boolean;
|
||||
width?: `${string}px` | 'auto' | number;
|
||||
}>`
|
||||
backdrop-filter: ${({ disableBlur }) =>
|
||||
disableBlur ? 'none' : 'blur(20px)'};
|
||||
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
width: ${({ width }) =>
|
||||
width ? `${typeof width === 'number' ? `${width}px` : width}` : '160px'};
|
||||
`;
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
import { PlayFunction } from '@storybook/types';
|
||||
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
@ -13,19 +14,17 @@ import { Avatar } from '@/users/components/Avatar';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { DropdownScope } from '../../scopes/DropdownScope';
|
||||
import { DropdownMenu } from '../DropdownMenu';
|
||||
import { Dropdown } from '../Dropdown';
|
||||
import { DropdownMenuHeader } from '../DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '../DropdownMenuInput';
|
||||
import { DropdownMenuInputContainer } from '../DropdownMenuInputContainer';
|
||||
import { DropdownMenuItemsContainer } from '../DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '../DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '../StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '../StyledDropdownMenuSeparator';
|
||||
import { DropdownMenuSeparator } from '../DropdownMenuSeparator';
|
||||
import { StyledDropdownMenuSubheader } from '../StyledDropdownMenuSubheader';
|
||||
|
||||
const meta: Meta<typeof DropdownMenu> = {
|
||||
title: 'UI/Layout/Dropdown/DropdownMenu',
|
||||
component: DropdownMenu,
|
||||
const meta: Meta<typeof Dropdown> = {
|
||||
title: 'UI/Layout/Dropdown/Dropdown',
|
||||
component: Dropdown,
|
||||
|
||||
decorators: [
|
||||
ComponentDecorator,
|
||||
@ -38,7 +37,7 @@ const meta: Meta<typeof DropdownMenu> = {
|
||||
args: {
|
||||
clickableComponent: <Button title="Open Dropdown" />,
|
||||
dropdownHotkeyScope: { scope: 'testDropdownMenu' },
|
||||
dropdownOffset: { x: 0, y: -8 },
|
||||
dropdownOffset: { x: 0, y: 8 },
|
||||
},
|
||||
argTypes: {
|
||||
clickableComponent: { control: false },
|
||||
@ -49,26 +48,9 @@ const meta: Meta<typeof DropdownMenu> = {
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DropdownMenu>;
|
||||
type Story = StoryObj<typeof Dropdown>;
|
||||
|
||||
const FakeContentBelow = () => (
|
||||
<div style={{ position: 'absolute' }}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
||||
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat.
|
||||
</div>
|
||||
);
|
||||
|
||||
const avatarUrl =
|
||||
'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4';
|
||||
|
||||
const StyledFakeMenuContent = styled.div`
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledFakeBelowContainer = styled.div`
|
||||
const StyledContainer = styled.div`
|
||||
height: 600px;
|
||||
position: relative;
|
||||
|
||||
@ -77,12 +59,62 @@ const StyledFakeBelowContainer = styled.div`
|
||||
|
||||
const StyledMenuAbsolutePositionWrapper = styled.div`
|
||||
height: fit-content;
|
||||
position: absolute;
|
||||
|
||||
width: fit-content;
|
||||
`;
|
||||
|
||||
const mockSelectArray = [
|
||||
const WithContentBelowDecorator: Decorator = (Story) => (
|
||||
<StyledContainer>
|
||||
<StyledMenuAbsolutePositionWrapper>
|
||||
<Story />
|
||||
</StyledMenuAbsolutePositionWrapper>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
||||
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat.
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
const StyledEmptyDropdownContent = styled.div`
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Empty: Story = {
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledEmptyDropdownContent data-testid="dropdown-content" />
|
||||
),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const button = await canvas.findByRole('button');
|
||||
userEvent.click(button);
|
||||
|
||||
await waitFor(async () => {
|
||||
const fakeMenu = await canvas.findByTestId('dropdown-content');
|
||||
expect(fakeMenu).toBeInTheDocument();
|
||||
});
|
||||
|
||||
userEvent.click(button);
|
||||
|
||||
await waitFor(async () => {
|
||||
const fakeMenu = await canvas.findByTestId('dropdown-content');
|
||||
expect(fakeMenu).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
userEvent.click(button);
|
||||
await waitFor(async () => {
|
||||
const fakeMenu = await canvas.findByTestId('dropdown-content');
|
||||
expect(fakeMenu).toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const avatarUrl =
|
||||
'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4';
|
||||
|
||||
const optionsMock = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Company A',
|
||||
@ -120,7 +152,7 @@ const FakeSelectableMenuItemList = ({ hasAvatar }: { hasAvatar?: boolean }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{mockSelectArray.map((item) => (
|
||||
{optionsMock.map((item) => (
|
||||
<MenuItemSelectAvatar
|
||||
key={item.id}
|
||||
selected={selectedItem === item.id}
|
||||
@ -149,7 +181,7 @@ const FakeCheckableMenuItemList = ({ hasAvatar }: { hasAvatar?: boolean }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{mockSelectArray.map((item) => (
|
||||
{optionsMock.map((item) => (
|
||||
<MenuItemMultiSelectAvatar
|
||||
key={item.id}
|
||||
selected={selectedItemsById[item.id]}
|
||||
@ -176,55 +208,40 @@ const FakeCheckableMenuItemList = ({ hasAvatar }: { hasAvatar?: boolean }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const WithContentBelowDecorator: Decorator = (Story) => (
|
||||
<StyledFakeBelowContainer>
|
||||
<FakeContentBelow />
|
||||
<StyledMenuAbsolutePositionWrapper>
|
||||
<Story />
|
||||
</StyledMenuAbsolutePositionWrapper>
|
||||
</StyledFakeBelowContainer>
|
||||
);
|
||||
|
||||
const playInteraction: PlayFunction<any, any> = async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const button = await canvas.findByRole('button');
|
||||
|
||||
userEvent.click(button);
|
||||
};
|
||||
|
||||
export const Empty: Story = {
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<StyledFakeMenuContent />
|
||||
</StyledDropdownMenu>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
await waitFor(async () => {
|
||||
expect(canvas.getByText('Company A')).toBeInTheDocument();
|
||||
});
|
||||
};
|
||||
|
||||
export const WithHeaders: Story = {
|
||||
decorators: [WithContentBelowDecorator],
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<>
|
||||
<DropdownMenuHeader>Header</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<StyledDropdownMenuSubheader>Subheader 1</StyledDropdownMenuSubheader>
|
||||
<DropdownMenuItemsContainer>
|
||||
{mockSelectArray.slice(0, 3).map(({ name }) => (
|
||||
<MenuItem text={name} />
|
||||
))}
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<>
|
||||
{optionsMock.slice(0, 3).map(({ name }) => (
|
||||
<MenuItem text={name} />
|
||||
))}
|
||||
</>
|
||||
</DropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<StyledDropdownMenuSubheader>Subheader 2</StyledDropdownMenuSubheader>
|
||||
<DropdownMenuItemsContainer>
|
||||
{mockSelectArray.slice(3).map(({ name }) => (
|
||||
{optionsMock.slice(3).map(({ name }) => (
|
||||
<MenuItem text={name} />
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
@ -234,33 +251,45 @@ export const SearchWithLoadingMenu: Story = {
|
||||
decorators: [WithContentBelowDecorator],
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<>
|
||||
<DropdownMenuSearchInput value="query" autoFocus />
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<DropdownMenuSkeletonItem />
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const button = await canvas.findByRole('button');
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(button);
|
||||
expect(canvas.getByDisplayValue('query')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(button);
|
||||
expect(canvas.queryByDisplayValue('query')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const WithInput: Story = {
|
||||
decorators: [WithContentBelowDecorator],
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<DropdownMenuInputContainer>
|
||||
<DropdownMenuInput defaultValue="Lorem ipsum" autoFocus />
|
||||
</DropdownMenuInputContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<>
|
||||
<DropdownMenuInput defaultValue="Lorem ipsum" autoFocus />
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{mockSelectArray.map(({ name }) => (
|
||||
{optionsMock.map(({ name }) => (
|
||||
<MenuItem text={name} />
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
@ -270,11 +299,9 @@ export const SelectableMenuItemWithAvatar: Story = {
|
||||
decorators: [WithContentBelowDecorator],
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<FakeSelectableMenuItemList hasAvatar />
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<FakeSelectableMenuItemList hasAvatar />
|
||||
</DropdownMenuItemsContainer>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
@ -284,11 +311,9 @@ export const CheckableMenuItemWithAvatar: Story = {
|
||||
decorators: [WithContentBelowDecorator],
|
||||
args: {
|
||||
dropdownComponents: (
|
||||
<StyledDropdownMenu>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<FakeCheckableMenuItemList hasAvatar />
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<FakeCheckableMenuItemList hasAvatar />
|
||||
</DropdownMenuItemsContainer>
|
||||
),
|
||||
},
|
||||
play: playInteraction,
|
||||
|
||||
@ -9,10 +9,6 @@ const meta: Meta<typeof DropdownMenuInput> = {
|
||||
component: DropdownMenuInput,
|
||||
decorators: [ComponentDecorator],
|
||||
args: { defaultValue: 'Lorem ipsum' },
|
||||
argTypes: {
|
||||
as: { table: { disable: true } },
|
||||
theme: { table: { disable: true } },
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
@ -3,15 +3,17 @@ import styled from '@emotion/styled';
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { ViewBarDropdownButton } from '@/ui/data/view-bar/components/ViewBarDropdownButton';
|
||||
import { IconCheckbox, IconNotes, IconPlus } from '@/ui/display/icon/index';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { ActivityType } from '~/generated/graphql';
|
||||
|
||||
import { Dropdown } from '../../dropdown/components/Dropdown';
|
||||
import { DropdownMenu } from '../../dropdown/components/DropdownMenu';
|
||||
import { DropdownScope } from '../../dropdown/scopes/DropdownScope';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
z-index: 1;
|
||||
`;
|
||||
@ -33,40 +35,41 @@ export const ShowPageAddButton = ({
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<ViewBarDropdownButton
|
||||
dropdownId="add-show-page"
|
||||
buttonComponent={
|
||||
<IconButton
|
||||
Icon={IconPlus}
|
||||
size="medium"
|
||||
dataTestId="add-showpage-button"
|
||||
accent="default"
|
||||
variant="secondary"
|
||||
onClick={toggleDropdown}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => handleSelect(ActivityType.Note)}
|
||||
accent="default"
|
||||
LeftIcon={IconNotes}
|
||||
text="Note"
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => handleSelect(ActivityType.Task)}
|
||||
accent="default"
|
||||
LeftIcon={IconCheckbox}
|
||||
text="Task"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: PageHotkeyScope.ShowPage,
|
||||
}}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId="add-show-page">
|
||||
<Dropdown
|
||||
clickableComponent={
|
||||
<IconButton
|
||||
Icon={IconPlus}
|
||||
size="medium"
|
||||
dataTestId="add-showpage-button"
|
||||
accent="default"
|
||||
variant="secondary"
|
||||
onClick={toggleDropdown}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenu>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => handleSelect(ActivityType.Note)}
|
||||
accent="default"
|
||||
LeftIcon={IconNotes}
|
||||
text="Note"
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => handleSelect(ActivityType.Task)}
|
||||
accent="default"
|
||||
LeftIcon={IconCheckbox}
|
||||
text="Task"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
}
|
||||
dropdownHotkeyScope={{
|
||||
scope: PageHotkeyScope.ShowPage,
|
||||
}}
|
||||
/>
|
||||
</DropdownScope>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,8 +2,8 @@ import React, { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
@ -73,7 +73,7 @@ export const ContextMenu = ({ selectedIds }: ContextMenuProps) => {
|
||||
ref={wrapperRef}
|
||||
position={contextMenuPosition}
|
||||
>
|
||||
<StyledDropdownMenu data-select-disable width={width}>
|
||||
<DropdownMenu data-select-disable width={width}>
|
||||
<DropdownMenuItemsContainer>
|
||||
{contextMenuEntries.map((item) => (
|
||||
<ContextMenuItem
|
||||
@ -85,7 +85,7 @@ export const ContextMenu = ({ selectedIds }: ContextMenuProps) => {
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
</DropdownMenu>
|
||||
</StyledContainerContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user