-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ref(issue-views): Restructure issue views components #82593
Conversation
const handleNewViewsSaved: NewTabContext['onNewViewsSaved'] = useCallback< | ||
NewTabContext['onNewViewsSaved'] | ||
>( | ||
() => (newViews: NewView[]) => { | ||
if (newViews.length === 0) { | ||
return; | ||
} | ||
setNewViewActive(false); | ||
const {label, query, saveQueryToView} = newViews[0]; | ||
const remainingNewViews: IssueView[] = newViews.slice(1)?.map(view => { | ||
const newId = generateTempViewId(); | ||
const viewToTab: IssueView = { | ||
id: newId, | ||
key: newId, | ||
label: view.label, | ||
query: view.query, | ||
querySort: IssueSortOptions.DATE, | ||
unsavedChanges: view.saveQueryToView | ||
? undefined | ||
: [view.query, IssueSortOptions.DATE], | ||
isCommitted: true, | ||
}; | ||
return viewToTab; | ||
}); | ||
let updatedTabs: IssueView[] = tabs.map(tab => { | ||
if (tab.key === viewId) { | ||
return { | ||
...tab, | ||
label, | ||
query: saveQueryToView ? query : '', | ||
querySort: IssueSortOptions.DATE, | ||
unsavedChanges: saveQueryToView ? undefined : [query, IssueSortOptions.DATE], | ||
isCommitted: true, | ||
}; | ||
} | ||
return tab; | ||
}); | ||
|
||
if (remainingNewViews.length > 0) { | ||
updatedTabs = [...updatedTabs, ...remainingNewViews]; | ||
} | ||
|
||
dispatch({ | ||
type: 'SET_VIEWS', | ||
views: updatedTabs, | ||
syncViews: true, | ||
updateQueryParams: { | ||
newQueryParams: {query, sort: IssueSortOptions.DATE}, | ||
replace: true, | ||
}, | ||
}); | ||
}, | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
[location, setNewViewActive, tabs, viewId] | ||
); | ||
|
||
useEffect(() => { | ||
setOnNewViewsSaved(handleNewViewsSaved); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to IssueViews.tsx
(context component)
This logic makes sure that the "save as view" and "save all" buttons on the new view page work properly.
useHotkeys( | ||
[ | ||
{ | ||
match: ['command+s', 'ctrl+s'], | ||
includeInputs: true, | ||
callback: () => { | ||
if (tabs.find(tab => tab.key === tabListState?.selectedKey)?.unsavedChanges) { | ||
dispatch({type: 'SAVE_CHANGES', syncViews: true}); | ||
addSuccessMessage(t('Changes saved to view')); | ||
} | ||
}, | ||
}, | ||
], | ||
[dispatch, tabListState?.selectedKey, tabs] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to customViewsHeader.tsx
This logic controls the ctrl/cmd+s to save behavior
<TabContentWrap> | ||
<EditableTabTitle | ||
label={tab.label} | ||
isEditing={editingTabKey === tab.key} | ||
setIsEditing={isEditing => setEditingTabKey(isEditing ? tab.key : null)} | ||
onChange={newLabel => | ||
dispatch({type: 'RENAME_TAB', newLabel: newLabel.trim(), syncViews: true}) | ||
} | ||
isSelected={ | ||
(tabListState && tabListState?.selectedKey === tab.key) || | ||
(!tabListState && tab.key === initialTabKey) | ||
} | ||
/> | ||
{/* If tablistState isn't initialized, we want to load the elipsis menu | ||
for the initial tab, that way it won't load in a second later | ||
and cause the tabs to shift and animate on load. | ||
*/} | ||
{((tabListState && tabListState?.selectedKey === tab.key) || | ||
(!tabListState && tab.key === initialTabKey)) && ( | ||
<motion.div | ||
// This stops the ellipsis menu from animating in on load (when tabListState isn't initialized yet), | ||
// but enables the animation later on when switching tabs | ||
initial={tabListState ? {opacity: 0} : false} | ||
animate={{opacity: 1}} | ||
transition={{delay: 0.1, duration: 0.1}} | ||
> | ||
<DraggableTabMenuButton | ||
hasUnsavedChanges={!!tab.unsavedChanges} | ||
menuOptions={makeMenuOptions(tab)} | ||
aria-label={t(`%s Ellipsis Menu`, tab.label)} | ||
/> | ||
</motion.div> | ||
)} | ||
</TabContentWrap> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to its own component IssueViewTab.tsx
This contains the tab's content like the editable title and ellipsis menu
|
||
const TEMPORARY_TAB_KEY = 'temporary-tab'; | ||
|
||
export const generateTempViewId = () => `_${Math.random().toString().substring(2, 7)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved in from draggableTabBar.tsx
- this is a general function used by a lot of components. It should live in the context component not some random component in the hierachy
Bundle ReportChanges will increase total bundle size by 68 bytes (0.0%) ⬆️. This is within the configured threshold ✅ Detailed changes
|
edc837e
to
ed6c6d5
Compare
ed6c6d5
to
90e2196
Compare
90e2196
to
757cfc0
Compare
This PR reorganizes some of the components and logic in IssueViews components. No visual or functional changes should are intended by these changes. If you see any in the preview, please lmk.
The biggest changes include:
draggableTabBar.tsx
component, redistribute it's parts to other components (detailed in the diff)IssueViewTab
component that contains the tab contents (title, ellipsis menu, etc.).draggableTabMenuButton.tsx
toissueViewEllipsisMenu.tsx
draggableTabs
toviews
incustomViewsHeader.tsx
Some reasoning:
DraggableTabBar
sounds very redundant to and less clear thanCustomViewsHeader
andDraggableTabList
. To me, CustomViewsHeader (soon to be renamed IssueViewsHeader) should be a specific implementation ofDraggableTabList
, which sounds like it should be reusable.