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 <[email protected]>
Co-authored-by: FellipeMTX <[email protected]>

* Fix the tests

Co-authored-by: Benjamin Mayanja V <[email protected]>
Co-authored-by: FellipeMTX <[email protected]>

* Simplify Dropdown

* Remove console.log

---------

Co-authored-by: Benjamin Mayanja V <[email protected]>
Co-authored-by: FellipeMTX <[email protected]>
Co-authored-by: Charles Bochet <[email protected]>
This commit is contained in:
gitstart-twenty
2023-10-20 21:19:43 +02:00
committed by GitHub
co-authored by Benjamin Mayanja V FellipeMTX Charles Bochet
parent eea7470571
commit dee9807eb3
41 changed files with 634 additions and 674 deletions
+13
View File
@@ -16,4 +16,17 @@ html {
.sbdocs-wrapper {
padding: 0 !important;
}
*::-webkit-scrollbar {
height: 4px;
width: 4px;
}
*::-webkit-scrollbar-corner {
background-color: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 2px;
}
</style>
+1 -1
View File
@@ -181,4 +181,4 @@
"msw": {
"workerDirectory": "public"
}
}
}
@@ -6,11 +6,11 @@ import { IconChevronDown } from '@/ui/display/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 { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
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 { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
@@ -79,7 +79,7 @@ export const CompanyProgressPicker = ({
);
return (
<StyledDropdownMenu
<DropdownMenu
ref={containerRef}
data-testid={`company-progress-dropdown-menu`}
>
@@ -105,13 +105,13 @@ export const CompanyProgressPicker = ({
>
{selectedPipelineStage?.name}
</DropdownMenuHeader>
<StyledDropdownMenuSeparator />
<DropdownMenuSeparator />
<DropdownMenuSearchInput
value={searchFilter}
onChange={handleSearchFilterChange}
autoFocus
/>
<StyledDropdownMenuSeparator />
<DropdownMenuSeparator />
<RecoilScope>
<SingleEntitySelectBase
entitiesToSelect={companies.entitiesToSelect}
@@ -123,6 +123,6 @@ export const CompanyProgressPicker = ({
</RecoilScope>
</>
)}
</StyledDropdownMenu>
</DropdownMenu>
);
};
@@ -6,8 +6,8 @@ import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react';
import { IconDotsVertical, IconLinkOff, IconTrash } from '@/ui/display/icon';
import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton';
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 { Avatar } from '@/users/components/Avatar';
@@ -164,7 +164,7 @@ export const PeopleCard = ({
Icon={IconDotsVertical}
/>
{isOptionsOpen && (
<StyledDropdownMenu
<DropdownMenu
data-select-disable
ref={refs.setFloating}
style={floatingStyles}
@@ -182,7 +182,7 @@ export const PeopleCard = ({
accent="danger"
/>
</DropdownMenuItemsContainer>
</StyledDropdownMenu>
</DropdownMenu>
)}
</div>
)}
@@ -1,13 +1,14 @@
import { CompanyProgressPicker } from '@/companies/components/CompanyProgressPicker';
import { useCreateCompanyProgress } from '@/companies/hooks/useCreateCompanyProgress';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { ViewBarDropdownButton } from '@/ui/data/view-bar/components/ViewBarDropdownButton';
import { IconPlus } from '@/ui/display/icon/index';
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
import { IconButton } from '@/ui/input/button/components/IconButton';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { logError } from '~/utils/logError';
export const PipelineAddButton = () => {
@@ -51,32 +52,33 @@ export const PipelineAddButton = () => {
};
return (
<ViewBarDropdownButton
dropdownId="add-pipeline-progress"
buttonComponent={
<IconButton
Icon={IconPlus}
size="medium"
dataTestId="add-company-progress-button"
accent="default"
variant="secondary"
onClick={toggleDropdown}
/>
}
dropdownComponents={
<CompanyProgressPicker
companyId={null}
onSubmit={handleCompanySelected}
onCancel={closeDropdown}
/>
}
hotkey={{
key: 'c',
scope: PageHotkeyScope.OpportunitiesPage,
}}
dropdownHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
<DropdownScope dropdownScopeId="add-pipeline-progress">
<Dropdown
clickableComponent={
<IconButton
Icon={IconPlus}
size="medium"
dataTestId="add-company-progress-button"
accent="default"
variant="secondary"
onClick={toggleDropdown}
/>
}
dropdownComponents={
<CompanyProgressPicker
companyId={null}
onSubmit={handleCompanySelected}
onCancel={closeDropdown}
/>
}
hotkey={{
key: 'c',
scope: PageHotkeyScope.OpportunitiesPage,
}}
dropdownHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
</DropdownScope>
);
};
@@ -1,9 +1,9 @@
import { IconDotsVertical, IconTrash } from '@/ui/display/icon';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { IconArchiveOff } from '@/ui/input/constants/icons';
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 { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
@@ -22,12 +22,12 @@ export const SettingsObjectDisabledMenuDropDown = ({
<DropdownScope
dropdownScopeId={scopeKey + '-settings-object-disabled-menu-dropdown'}
>
<DropdownMenu
<Dropdown
clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
}
dropdownComponents={
<StyledDropdownMenu width="160px">
<DropdownMenu width="160px">
<DropdownMenuItemsContainer>
<MenuItem
text="Activate"
@@ -41,7 +41,7 @@ export const SettingsObjectDisabledMenuDropDown = ({
onClick={handleErase}
/>
</DropdownMenuItemsContainer>
</StyledDropdownMenu>
</DropdownMenu>
}
dropdownHotkeyScope={{
scope: scopeKey + '-settings-object-disabled-menu-dropdown',
@@ -14,10 +14,10 @@ import { ReadonlyDeep } from 'type-fest';
import { SelectOption } from '@/spreadsheet-import/types';
import { AppTooltip } from '@/ui/display/tooltip/AppTooltip';
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 { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
@@ -116,7 +116,7 @@ export const MatchColumnSelect = ({
{isOpen &&
createPortal(
<StyledFloatingDropdown ref={refs.setFloating} style={floatingStyles}>
<StyledDropdownMenu
<DropdownMenu
data-select-disable
ref={dropdownContainerRef}
width={refs.domReference.current?.clientWidth}
@@ -126,7 +126,7 @@ export const MatchColumnSelect = ({
onChange={handleFilterChange}
autoFocus
/>
<StyledDropdownMenuSeparator />
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight>
{options?.map((option) => (
<>
@@ -156,7 +156,7 @@ export const MatchColumnSelect = ({
))}
{options?.length === 0 && <MenuItem text="No result" />}
</DropdownMenuItemsContainer>
</StyledDropdownMenu>
</DropdownMenu>
</StyledFloatingDropdown>,
document.body,
)}
@@ -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>
</>
);
};
@@ -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>
);
};