Improve viewbar api (#2233)
* create scopes * fix import bug * add useView hook * wip * wip * currentViewId is now retrieved via useView * working on sorts with useView * refactor in progress * refactor in progress * refactor in progress * refactor in progress * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fix code * fix code * wip * push * Fix issue dependencies * Fix resize --------- Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
This commit is contained in:
@ -1,123 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { ViewBar } from '@/ui/data/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { savedBoardColumnsState } from '../states/savedBoardColumnsState';
|
||||
import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector';
|
||||
import { canPersistBoardColumnsSelector } from '../states/selectors/canPersistBoardColumnsSelector';
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
import { BoardOptionsDropdown } from './BoardOptionsDropdown';
|
||||
|
||||
export type BoardHeaderProps = {
|
||||
className?: string;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
};
|
||||
|
||||
export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
|
||||
const BoardRecoilScopeContext =
|
||||
useContext(BoardContext).BoardRecoilScopeContext;
|
||||
|
||||
const ViewBarRecoilScopeContext =
|
||||
useContext(ViewBarContext).ViewBarRecoilScopeContext;
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const canPersistBoardCardFields = useRecoilValue(
|
||||
canPersistBoardCardFieldsScopedFamilySelector({
|
||||
recoilScopeId: boardRecoilScopeId,
|
||||
viewId: currentViewId,
|
||||
}),
|
||||
);
|
||||
const canPersistBoardColumns = useRecoilValue(canPersistBoardColumnsSelector);
|
||||
|
||||
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const [savedBoardCardFields, setSavedBoardCardFields] = useRecoilState(
|
||||
savedBoardCardFieldsFamilyState(currentViewId),
|
||||
);
|
||||
|
||||
const [_, setSearchParams] = useSearchParams();
|
||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||
const [, setSavedBoardColumns] = useRecoilState(savedBoardColumnsState);
|
||||
|
||||
const savedBoardColumns = useRecoilValue(savedBoardColumnsState);
|
||||
|
||||
const handleViewBarReset = () => {
|
||||
setBoardCardFields(savedBoardCardFields);
|
||||
setBoardColumns(savedBoardColumns);
|
||||
};
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const savedBoardCardFields = await snapshot.getPromise(
|
||||
savedBoardCardFieldsFamilyState(viewId),
|
||||
);
|
||||
set(
|
||||
boardCardFieldsScopedState(boardRecoilScopeId),
|
||||
savedBoardCardFields,
|
||||
);
|
||||
setSearchParams({ view: viewId });
|
||||
},
|
||||
[boardRecoilScopeId, setSearchParams],
|
||||
);
|
||||
|
||||
const handleCurrentViewSubmit = async () => {
|
||||
if (canPersistBoardCardFields) {
|
||||
setSavedBoardCardFields(boardCardFields);
|
||||
}
|
||||
if (canPersistBoardColumns) {
|
||||
setSavedBoardColumns(boardColumns);
|
||||
}
|
||||
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
const canPersistView = canPersistBoardCardFields || canPersistBoardColumns;
|
||||
|
||||
return (
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistView,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
className={className}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
}
|
||||
optionsDropdownScopeId={BoardScopeIds.OptionsDropdown}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,4 @@
|
||||
import { useResetRecoilState } from 'recoil';
|
||||
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
|
||||
import { Dropdown } from '../../dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '../../dropdown/scopes/DropdownScope';
|
||||
@ -21,7 +19,7 @@ export const BoardOptionsDropdown = ({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
}: BoardOptionsDropdownProps) => {
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
const { setViewEditMode } = useView();
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={BoardScopeIds.OptionsDropdown}>
|
||||
@ -34,7 +32,7 @@ export const BoardOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
onClickOutside={resetViewEditMode}
|
||||
onClickOutside={() => setViewEditMode('none')}
|
||||
dropdownMenuWidth={170}
|
||||
/>
|
||||
</DropdownScope>
|
||||
|
||||
@ -1,19 +1,9 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import {
|
||||
useRecoilCallback,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
useResetRecoilState,
|
||||
} from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/data/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/data/view-bar/hooks/useUpsertView';
|
||||
import { currentViewScopedSelector } from '@/ui/data/view-bar/states/selectors/currentViewScopedSelector';
|
||||
import { viewsByIdScopedSelector } from '@/ui/data/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import {
|
||||
IconBaselineDensitySmall,
|
||||
IconChevronLeft,
|
||||
@ -35,6 +25,10 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState';
|
||||
|
||||
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
@ -63,6 +57,8 @@ export const BoardOptionsDropdownContent = ({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
}: BoardOptionsDropdownContentProps) => {
|
||||
const { setViewEditMode, createView, currentViewId } = useView();
|
||||
const { viewEditMode, currentView } = useViewInternalStates();
|
||||
const { BoardRecoilScopeContext } = useContext(BoardContext);
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
@ -90,17 +86,6 @@ export const BoardOptionsDropdownContent = ({
|
||||
);
|
||||
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||
|
||||
const viewsById = useRecoilScopedValue(
|
||||
viewsByIdScopedSelector,
|
||||
BoardRecoilScopeContext, // TODO: replace with ViewBarRecoilScopeContext
|
||||
);
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
|
||||
const handleStageSubmit = () => {
|
||||
if (currentMenu !== 'stage-creation' || !stageInputRef?.current?.value)
|
||||
return;
|
||||
@ -119,23 +104,28 @@ export const BoardOptionsDropdownContent = ({
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const { upsertView } = useUpsertView();
|
||||
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const viewEditMode = snapshot
|
||||
.getLoadable(viewEditModeScopedState({ scopeId: boardRecoilScopeId }))
|
||||
.getValue();
|
||||
if (!viewEditMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const boardCardFields = await snapshot.getPromise(
|
||||
boardCardFieldsScopedState(boardRecoilScopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const isCreateMode = viewEditMode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
if (view && isCreateMode) {
|
||||
set(savedBoardCardFieldsFamilyState(view.id), boardCardFields);
|
||||
if (isCreateMode && name) {
|
||||
await createView(name);
|
||||
set(savedBoardCardFieldsFamilyState(currentViewId), boardCardFields);
|
||||
}
|
||||
},
|
||||
[boardRecoilScopeId, upsertView, viewEditMode.mode],
|
||||
[boardRecoilScopeId, createView, currentViewId],
|
||||
);
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
@ -152,7 +142,7 @@ export const BoardOptionsDropdownContent = ({
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
resetViewEditMode();
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
@ -163,7 +153,6 @@ export const BoardOptionsDropdownContent = ({
|
||||
() => {
|
||||
handleStageSubmit();
|
||||
handleViewNameSubmit();
|
||||
resetViewEditMode();
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
@ -173,20 +162,26 @@ export const BoardOptionsDropdownContent = ({
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<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
|
||||
}
|
||||
/>
|
||||
{viewEditMode && (
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode === 'create'
|
||||
? ''
|
||||
: viewEditMode === 'edit'
|
||||
? currentView?.name
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
|
||||
@ -6,7 +6,6 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { BoardHeader } from '@/ui/layout/board/components/BoardHeader';
|
||||
import { StyledBoard } from '@/ui/layout/board/components/StyledBoard';
|
||||
import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContext';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
@ -44,14 +43,13 @@ const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledBoardHeader = styled(BoardHeader)`
|
||||
const StyledBoardHeader = styled.div`
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
` as typeof BoardHeader;
|
||||
`;
|
||||
|
||||
export const EntityBoard = ({
|
||||
boardOptions,
|
||||
onColumnAdd,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
}: EntityBoardProps) => {
|
||||
@ -147,7 +145,7 @@ export const EntityBoard = ({
|
||||
|
||||
return (boardColumns?.length ?? 0) > 0 ? (
|
||||
<StyledWrapper>
|
||||
<StyledBoardHeader onStageAdd={onColumnAdd} />
|
||||
<StyledBoardHeader />
|
||||
<ScrollWrapper>
|
||||
<StyledBoard ref={boardRef}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
|
||||
import { BoardOptionsDropdown } from '../BoardOptionsDropdown';
|
||||
|
||||
const meta: Meta<typeof BoardOptionsDropdown> = {
|
||||
title: 'UI/Layout/Board/Options/BoardOptionsDropdown',
|
||||
component: BoardOptionsDropdown,
|
||||
decorators: [
|
||||
(Story, { parameters }) => (
|
||||
<BoardContext.Provider
|
||||
value={{
|
||||
BoardRecoilScopeContext: parameters.customRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
ViewBarRecoilScopeContext: parameters.customRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ViewBarContext.Provider>
|
||||
</BoardContext.Provider>
|
||||
),
|
||||
ComponentWithRecoilScopeDecorator,
|
||||
ComponentDecorator,
|
||||
],
|
||||
parameters: {
|
||||
customRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
},
|
||||
args: {
|
||||
customHotkeyScope: { scope: 'scope' },
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof BoardOptionsDropdown>;
|
||||
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const dropdownButton = canvas.getByText('Options');
|
||||
|
||||
await userEvent.click(dropdownButton);
|
||||
},
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { ViewFieldForVisibility } from '@/ui/data/view-bar/types/ViewFieldForVisibility';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { ViewFieldForVisibility } from '@/views/types/ViewFieldForVisibility';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
||||
import { useMoveViewColumns } from '@/ui/data/data-table/hooks/useMoveViewColumns';
|
||||
import { useUpdatePipelineStageMutation } from '~/generated/graphql';
|
||||
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import { FilterDefinitionByEntity } from '@/ui/data/view-bar/types/FilterDefinitionByEntity';
|
||||
import { SortDefinition } from '@/ui/data/view-bar/types/SortDefinition';
|
||||
import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity';
|
||||
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
|
||||
import { PipelineProgress } from '~/generated/graphql';
|
||||
|
||||
export type BoardOptions = {
|
||||
|
||||
@ -56,11 +56,11 @@ export const useDropdown = (props?: UseDropdownProps) => {
|
||||
};
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
isDropdownOpen: isDropdownOpen,
|
||||
closeDropdown: closeDropdownButton,
|
||||
toggleDropdown: toggleDropdownButton,
|
||||
openDropdown: openDropdownButton,
|
||||
scopeId,
|
||||
dropdownHotkeyScope,
|
||||
setDropdownHotkeyScope,
|
||||
dropdownWidth,
|
||||
|
||||
Reference in New Issue
Block a user