From 5b463534b5a4ade6558cedb67dd7d3f6ad1eb12d Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 5 Jan 2024 12:12:20 +0100 Subject: [PATCH 1/8] add variant=modal --- src/drafts/SelectPanel2/SelectPanel.tsx | 8 +-- .../stories/SelectPanel.features.stories.tsx | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index 10404a96f87..c4a258993b5 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -45,6 +45,7 @@ const SelectPanelContext = React.createContext<{ export type SelectPanelProps = { title: string description?: string + variant?: 'anchored' | 'modal' selectionVariant?: ActionListProps['selectionVariant'] | 'instant' id?: string @@ -66,6 +67,7 @@ export type SelectPanelProps = { const Panel: React.FC = ({ title, description, + variant = 'anchored', selectionVariant = 'multiple', id, @@ -199,12 +201,12 @@ const Panel: React.FC = ({ width={width} height={height} sx={{ - ...position, // reset dialog default styles border: 'none', padding: 0, - margin: 0, - '::backdrop': {background: 'transparent'}, + + ...(variant === 'anchored' ? {margin: 0, top: position?.top, left: position?.left} : {}), + '::backdrop': {backgroundColor: variant === 'anchored' ? 'transparent' : 'primer.canvas.backdrop'}, '& [data-selectpanel-primary-actions]': { animation: footerAnimationEnabled ? 'selectpanel-gelatine 350ms linear' : 'none', diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.features.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.features.stories.tsx index 255edd3a9d2..6ef7d694527 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.features.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.features.stories.tsx @@ -384,3 +384,56 @@ export const ExternalAnchor = () => { ) } + +export const AsModal = () => { + const initialSelectedLabels = data.issue.labelIds // mock initial state: has selected labels + const [selectedLabelIds, setSelectedLabelIds] = React.useState(initialSelectedLabels) + + /* Selection */ + const onLabelSelect = (labelId: string) => { + if (!selectedLabelIds.includes(labelId)) setSelectedLabelIds([...selectedLabelIds, labelId]) + else setSelectedLabelIds(selectedLabelIds.filter(id => id !== labelId)) + } + + const onSubmit = () => { + data.issue.labelIds = selectedLabelIds // pretending to persist changes + + // eslint-disable-next-line no-console + console.log('form submitted') + } + + const sortingFn = (itemA: {id: string}, itemB: {id: string}) => { + const initialSelectedIds = data.issue.labelIds + if (initialSelectedIds.includes(itemA.id) && initialSelectedIds.includes(itemB.id)) return 1 + else if (initialSelectedIds.includes(itemA.id)) return -1 + else if (initialSelectedIds.includes(itemB.id)) return 1 + else return 1 + } + + const itemsToShow = data.labels.sort(sortingFn) + + return ( + <> +

SelectPanel as Modal

+ + + Assign label + + + {itemsToShow.map(label => ( + onLabelSelect(label.id)} + selected={selectedLabelIds.includes(label.id)} + > + {getCircle(label.color)} + {label.name} + {label.description} + + ))} + + + + + ) +} From 37d1956152c647d1f617aa4bb325de29911b71d7 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 5 Jan 2024 12:52:42 +0100 Subject: [PATCH 2/8] update "open from menu" story example --- src/drafts/SelectPanel2/SelectPanel.tsx | 1 + .../stories/SelectPanel.examples.stories.tsx | 73 +++++++------------ 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index c4a258993b5..c11db05d523 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -156,6 +156,7 @@ const Panel: React.FC = ({ else dialogRef.current?.close() // dialog handles Esc automatically, so we have to sync internal state + // TODO: if it is submit event, calling onInternalClose calls propsOnCancel! React.useEffect(() => dialogRef.current?.addEventListener('close', onInternalClose)) // React doesn't support autoFocus for dialog: https://github.com/facebook/react/issues/23301 diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx index 81698bdb900..c67e7bc8fb9 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx @@ -370,56 +370,27 @@ export const OpenFromMenu = () => { /* Open state */ const [menuOpen, setMenuOpen] = React.useState(false) const [selectPanelOpen, setSelectPanelOpen] = React.useState(false) - const buttonRef = React.useRef(null) /* Selection */ const [selectedSetting, setSelectedSetting] = React.useState('All activity') - const [selectedEvents, setSelectedEvents] = React.useState([]) + const initialCustomEvents: string[] = [] + const [selectedCustomEvents, setSelectedCustomEvents] = React.useState(initialCustomEvents) const onEventSelect = (event: string) => { - if (!selectedEvents.includes(event)) setSelectedEvents([...selectedEvents, event]) - else setSelectedEvents(selectedEvents.filter(name => name !== event)) - } - - const onSelectPanelSubmit = () => { - setSelectedSetting('Custom') + if (!selectedCustomEvents.includes(event)) setSelectedCustomEvents([...selectedCustomEvents, event]) + else setSelectedCustomEvents(selectedCustomEvents.filter(name => name !== event)) } const itemsToShow = ['Issues', 'Pull requests', 'Releases', 'Discussions', 'Security alerts'] return ( <> -

Open from ActionMenu

- - - This implementation will most likely change.{' '} - - See decision log for more details. - - -

- To open SelectPanel from a menu, you would need to use an external anchor and pass `anchorRef` to `SelectPanel`. - You would also need to control the `open` state for both ActionMenu and SelectPanel. -
-
- Important: Pass the same `anchorRef` to both ActionMenu and SelectPanel -

- - - setMenuOpen(value)}> +

Open in modal from ActionMenu

+ + setMenuOpen(value)}> + + {selectedSetting === 'Ignore' ? 'Watch' : 'Unwatch'} + { Ignore Never be notified. - setSelectPanelOpen(true)}> + { + setMenuOpen(false) + setSelectPanelOpen(true) + }} + > Custom @@ -456,24 +433,30 @@ export const OpenFromMenu = () => { - { + setSelectedSetting('Custom') setSelectPanelOpen(false) - onSelectPanelSubmit() + setMenuOpen(false) }} onCancel={() => { + alert('cancel called') + setSelectedCustomEvents(initialCustomEvents) setSelectPanelOpen(false) setMenuOpen(true) }} - height="medium" > {itemsToShow.map(item => ( - onEventSelect(item)} selected={selectedEvents.includes(item)}> + onEventSelect(item)} + selected={selectedCustomEvents.includes(item)} + > {item} ))} From 6e59b1aa0544721c71edc2336c5f9fb62dc942e9 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 5 Jan 2024 12:53:05 +0100 Subject: [PATCH 3/8] remove unused imports --- .../SelectPanel2/stories/SelectPanel.examples.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx index c67e7bc8fb9..f05f57e24d6 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx @@ -1,7 +1,7 @@ import React from 'react' import {SelectPanel} from '../SelectPanel' -import {ActionList, ActionMenu, Avatar, Box, Button, Flash} from '../../../index' -import {ArrowRightIcon, AlertIcon, EyeIcon, GitBranchIcon, TriangleDownIcon, GearIcon} from '@primer/octicons-react' +import {ActionList, ActionMenu, Avatar, Box, Button} from '../../../index' +import {ArrowRightIcon, EyeIcon, GitBranchIcon, TriangleDownIcon, GearIcon} from '@primer/octicons-react' import data from './mock-data' export default { From 8468fa770ef68d9cd92d6a063de708fa6e6e0f84 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 5 Jan 2024 13:06:34 +0100 Subject: [PATCH 4/8] clean up effect --- src/drafts/SelectPanel2/SelectPanel.tsx | 9 +++++++-- .../stories/SelectPanel.examples.stories.tsx | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index c11db05d523..cda3cad07d3 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -121,6 +121,7 @@ const Panel: React.FC = ({ } const onInternalSubmit = (event?: React.FormEvent) => { + // TODO: do we still need prevent with ? event?.preventDefault() // there is no event with selectionVariant=instant if (propsOpen === undefined) setInternalOpen(false) if (typeof propsOnSubmit === 'function') propsOnSubmit(event) @@ -156,8 +157,12 @@ const Panel: React.FC = ({ else dialogRef.current?.close() // dialog handles Esc automatically, so we have to sync internal state - // TODO: if it is submit event, calling onInternalClose calls propsOnCancel! - React.useEffect(() => dialogRef.current?.addEventListener('close', onInternalClose)) + // TODO: Bug! if it is submit event, calling onInternalClose calls propsOnCancel! + React.useEffect(() => { + const dialog = dialogRef.current + dialog?.addEventListener('close', onInternalClose) + return () => dialog?.removeEventListener('close', onInternalClose) + }) // React doesn't support autoFocus for dialog: https://github.com/facebook/react/issues/23301 // tl;dr: react takes over autofocus instead of letting the browser handle it, diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx index f05f57e24d6..6805bd377f0 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx @@ -444,7 +444,7 @@ export const OpenFromMenu = () => { setMenuOpen(false) }} onCancel={() => { - alert('cancel called') + console.log('bug cancel called on submit!') setSelectedCustomEvents(initialCustomEvents) setSelectPanelOpen(false) setMenuOpen(true) From cea65b68cb83043e0ff8f72bec4525c9e4601ea6 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 5 Jan 2024 13:22:24 +0100 Subject: [PATCH 5/8] add note to continue --- src/drafts/SelectPanel2/SelectPanel.tsx | 2 ++ .../SelectPanel2/stories/SelectPanel.examples.stories.tsx | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index cda3cad07d3..863b687222c 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -154,6 +154,8 @@ const Panel: React.FC = ({ /* Dialog */ const dialogRef = React.useRef(null) if (internalOpen) dialogRef.current?.showModal() + // Continue here: TODO/BUG: we have an event listener for close, + // which calls onInternalCancel -> propsOnCancel even on submit else dialogRef.current?.close() // dialog handles Esc automatically, so we have to sync internal state diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx index 6805bd377f0..5bf630d0fdf 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx @@ -444,7 +444,8 @@ export const OpenFromMenu = () => { setMenuOpen(false) }} onCancel={() => { - console.log('bug cancel called on submit!') + console.log('bug: onCancel is called on submit!') + console.log('bug: onCancel is called twice on cancel!') setSelectedCustomEvents(initialCustomEvents) setSelectPanelOpen(false) setMenuOpen(true) From ece301309a1d7b2f3a89f90cb25c3c7040b9c31e Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Sat, 13 Jan 2024 02:17:33 +0530 Subject: [PATCH 6/8] remove unrelated comments --- src/drafts/SelectPanel2/SelectPanel.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index 863b687222c..aefffb9f3d2 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -121,7 +121,6 @@ const Panel: React.FC = ({ } const onInternalSubmit = (event?: React.FormEvent) => { - // TODO: do we still need prevent with ? event?.preventDefault() // there is no event with selectionVariant=instant if (propsOpen === undefined) setInternalOpen(false) if (typeof propsOnSubmit === 'function') propsOnSubmit(event) @@ -154,12 +153,9 @@ const Panel: React.FC = ({ /* Dialog */ const dialogRef = React.useRef(null) if (internalOpen) dialogRef.current?.showModal() - // Continue here: TODO/BUG: we have an event listener for close, - // which calls onInternalCancel -> propsOnCancel even on submit else dialogRef.current?.close() // dialog handles Esc automatically, so we have to sync internal state - // TODO: Bug! if it is submit event, calling onInternalClose calls propsOnCancel! React.useEffect(() => { const dialog = dialogRef.current dialog?.addEventListener('close', onInternalClose) From 0c6ebd47286aa3fe8112480b2811f5f3a89115cb Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Sat, 13 Jan 2024 02:18:03 +0530 Subject: [PATCH 7/8] Create long-pumas-shout.md --- .changeset/long-pumas-shout.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/long-pumas-shout.md diff --git a/.changeset/long-pumas-shout.md b/.changeset/long-pumas-shout.md new file mode 100644 index 00000000000..3e389b04f95 --- /dev/null +++ b/.changeset/long-pumas-shout.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +experimental/SelectPanel: Add `modal` variant From 8eee364acc4f852301b2e2a3de3bbffda79ff389 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Sat, 13 Jan 2024 02:22:34 +0530 Subject: [PATCH 8/8] remove debug statement --- .../SelectPanel2/stories/SelectPanel.examples.stories.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx index 5bf630d0fdf..e9c8087fa0f 100644 --- a/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx +++ b/src/drafts/SelectPanel2/stories/SelectPanel.examples.stories.tsx @@ -444,8 +444,6 @@ export const OpenFromMenu = () => { setMenuOpen(false) }} onCancel={() => { - console.log('bug: onCancel is called on submit!') - console.log('bug: onCancel is called twice on cancel!') setSelectedCustomEvents(initialCustomEvents) setSelectPanelOpen(false) setMenuOpen(true)