From fdef7c92d1f4a83c58724fee1787a67c0d4ca30a Mon Sep 17 00:00:00 2001 From: hectahertz Date: Tue, 18 Mar 2025 19:33:48 +0100 Subject: [PATCH 1/6] Add notice to SelectPanel --- .../src/SelectPanel/SelectPanel.module.css | 37 ++++++++++++ .../react/src/SelectPanel/SelectPanel.tsx | 59 ++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/react/src/SelectPanel/SelectPanel.module.css b/packages/react/src/SelectPanel/SelectPanel.module.css index e607edba2a1..eeef8791d6d 100644 --- a/packages/react/src/SelectPanel/SelectPanel.module.css +++ b/packages/react/src/SelectPanel/SelectPanel.module.css @@ -20,6 +20,43 @@ color: var(--fgColor-muted); } +.Notice { + margin-top: var(--base-size-4); + display: flex; + flex-direction: row; + gap: var(--base-size-8); + padding-top: var(--base-size-12); + padding-right: var(--base-size-24); + padding-left: var(--base-size-24); + padding-bottom: var(--base-size-12); + border-top: 1px solid; + border-bottom: 1px solid; + font-size: var(--text-body-size-small); +} + +.Notice a { + color: inherit; + text-decoration: underline; +} + +.Notice:where([data-variant='info']) { + color: var(--fgColor-accent); + background-color: var(--bgColor-accent-muted); + border-color: var(--borderColor-accent-muted); +} + +.Notice:where([data-variant='warning']) { + color: var(--fgColor-attention); + background-color: var(--bgColor-attention-muted); + border-color: var(--borderColor-attention-muted); +} + +.Notice:where([data-variant='error']) { + color: var(--fgColor-danger); + background-color: var(--bgColor-danger-muted); + border-color: var(--borderColor-danger-muted); +} + .Footer { display: flex; padding: var(--base-size-8); diff --git a/packages/react/src/SelectPanel/SelectPanel.tsx b/packages/react/src/SelectPanel/SelectPanel.tsx index 396bb717309..4b05f5cc22b 100644 --- a/packages/react/src/SelectPanel/SelectPanel.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.tsx @@ -1,4 +1,4 @@ -import {SearchIcon, TriangleDownIcon} from '@primer/octicons-react' +import {AlertIcon, InfoIcon, SearchIcon, StopIcon, TriangleDownIcon} from '@primer/octicons-react' import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react' import type {AnchoredOverlayProps} from '../AnchoredOverlay' import {AnchoredOverlay} from '../AnchoredOverlay' @@ -128,6 +128,10 @@ interface SelectPanelBaseProps { footer?: string | React.ReactElement initialLoadingType?: InitialLoadingType className?: string + notice?: { + text: string | React.ReactElement + variant: 'info' | 'warning' | 'error' + } } export type SelectPanelProps = SelectPanelBaseProps & @@ -189,6 +193,7 @@ export function SelectPanel({ height, width, id, + notice, ...listProps }: SelectPanelProps): JSX.Element { const titleId = useId() @@ -435,6 +440,30 @@ export function SelectPanel({ } const usingModernActionList = useFeatureFlag('primer_react_select_panel_with_modern_action_list') + const stylesForNoticeVariant = { + info: { + backgroundColor: 'accent.subtle', + color: 'accent.fg', + borderColor: 'accent.muted', + }, + warning: { + backgroundColor: 'attention.subtle', + color: 'attention.fg', + borderColor: 'attention.muted', + }, + error: { + backgroundColor: 'danger.subtle', + color: 'danger.fg', + borderColor: 'danger.muted', + }, + } + + const iconForNoticeVariant = { + info: , + warning: , + error: , + } + return ( ) : null} + {notice && + (enabled ? ( +
+ {iconForNoticeVariant[notice.variant]} +
{notice.text}
+
+ ) : ( + + {iconForNoticeVariant[notice.variant]} + {notice.text} + + ))} Date: Tue, 18 Mar 2025 19:33:56 +0100 Subject: [PATCH 2/6] Add stories for the SelectPanel Notice --- .../SelectPanel.features.stories.tsx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/packages/react/src/SelectPanel/SelectPanel.features.stories.tsx b/packages/react/src/SelectPanel/SelectPanel.features.stories.tsx index 1c8e5719a6a..e8e29a89d35 100644 --- a/packages/react/src/SelectPanel/SelectPanel.features.stories.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.features.stories.tsx @@ -5,17 +5,23 @@ import {Button} from '../Button' import type {ItemInput, GroupedListProps} from '../deprecated/ActionList/List' import {SelectPanel, type SelectPanelProps} from './SelectPanel' import { + AlertIcon, FilterIcon, GearIcon, + InfoIcon, NoteIcon, ProjectIcon, SearchIcon, + StopIcon, TriangleDownIcon, TypographyIcon, VersionsIcon, } from '@primer/octicons-react' import useSafeTimeout from '../hooks/useSafeTimeout' import FormControl from '../FormControl' +import Link from '../Link' +import {SegmentedControl} from '../SegmentedControl' +import {Stack} from '../Stack' const meta = { title: 'Components/SelectPanel/Features', @@ -311,6 +317,92 @@ export const WithFooter = () => { ) } +export const WithNotice = () => { + const [selected, setSelected] = useState(items.slice(1, 3)) + const [filter, setFilter] = useState('') + const filteredItems = items.filter( + item => + // design guidelines say to always show selected items in the list + selected.some(selectedItem => selectedItem.text === item.text) || + // then filter the rest + item.text.toLowerCase().startsWith(filter.toLowerCase()), + ) + // design guidelines say to sort selected items first + const selectedItemsSortedFirst = filteredItems.sort((a, b) => { + const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text) + const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text) + if (aIsSelected && !bIsSelected) return -1 + if (!aIsSelected && bIsSelected) return 1 + return 0 + }) + const [open, setOpen] = useState(false) + const [noticeVariant, setNoticeVariant] = useState(0) + + const noticeVariants: Array<{text: string | React.ReactElement; variant: 'info' | 'warning' | 'error'}> = [ + { + variant: 'info', + text: 'Try a different search term.', + }, + { + variant: 'warning', + text: ( + <> + You have reached the limit of assignees on your free account.{' '} + Upgrade your account. + + ), + }, + { + variant: 'error', + text: ( + <> + We couldn't load all collaborators. Try again or if the problem persists,{' '} + contact support + + ), + }, + ] + + return ( + + + Notice variant + + + Info notice + + + Warning notice + + + Error notice + + + + + SelectPanel with notice + ( + + )} + placeholder="Select labels" // button text when no items are selected + open={open} + onOpenChange={setOpen} + items={selectedItemsSortedFirst} + selected={selected} + onSelectedChange={setSelected} + onFilterChange={setFilter} + overlayProps={{width: 'small', height: 'medium'}} + width="medium" + notice={noticeVariants[noticeVariant]} + /> + + + ) +} + const listOfItems: Array = [ { id: '1', From edff839c09b8321183c15ca78e000de49bff3787 Mon Sep 17 00:00:00 2001 From: hectahertz Date: Tue, 18 Mar 2025 19:36:42 +0100 Subject: [PATCH 3/6] Add changeset --- .changeset/slick-teams-check.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slick-teams-check.md diff --git a/.changeset/slick-teams-check.md b/.changeset/slick-teams-check.md new file mode 100644 index 00000000000..c265f8ac6b4 --- /dev/null +++ b/.changeset/slick-teams-check.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Add Notice to SelectPanel From da6dc93f713f54c68aa96486627a20e9809a6516 Mon Sep 17 00:00:00 2001 From: hectahertz Date: Tue, 18 Mar 2025 19:51:48 +0100 Subject: [PATCH 4/6] Refine paddings --- packages/react/src/SelectPanel/SelectPanel.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/SelectPanel/SelectPanel.module.css b/packages/react/src/SelectPanel/SelectPanel.module.css index eeef8791d6d..6bbe5b3531b 100644 --- a/packages/react/src/SelectPanel/SelectPanel.module.css +++ b/packages/react/src/SelectPanel/SelectPanel.module.css @@ -26,8 +26,8 @@ flex-direction: row; gap: var(--base-size-8); padding-top: var(--base-size-12); - padding-right: var(--base-size-24); - padding-left: var(--base-size-24); + padding-right: var(--base-size-16); + padding-left: var(--base-size-16); padding-bottom: var(--base-size-12); border-top: 1px solid; border-bottom: 1px solid; From eeb013ec2d08832874adf93a967e44a59a3c3eb5 Mon Sep 17 00:00:00 2001 From: hectahertz Date: Wed, 19 Mar 2025 16:12:04 +0100 Subject: [PATCH 5/6] Remove StyledComponents code on the SelectPanel Notice --- .../react/src/SelectPanel/SelectPanel.tsx | 52 +++---------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/packages/react/src/SelectPanel/SelectPanel.tsx b/packages/react/src/SelectPanel/SelectPanel.tsx index 4b05f5cc22b..250aefe1748 100644 --- a/packages/react/src/SelectPanel/SelectPanel.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.tsx @@ -440,24 +440,6 @@ export function SelectPanel({ } const usingModernActionList = useFeatureFlag('primer_react_select_panel_with_modern_action_list') - const stylesForNoticeVariant = { - info: { - backgroundColor: 'accent.subtle', - color: 'accent.fg', - borderColor: 'accent.muted', - }, - warning: { - backgroundColor: 'attention.subtle', - color: 'attention.fg', - borderColor: 'attention.muted', - }, - error: { - backgroundColor: 'danger.subtle', - color: 'danger.fg', - borderColor: 'danger.muted', - }, - } - const iconForNoticeVariant = { info: , warning: , @@ -520,34 +502,12 @@ export function SelectPanel({ ) : null} - {notice && - (enabled ? ( -
- {iconForNoticeVariant[notice.variant]} -
{notice.text}
-
- ) : ( - - {iconForNoticeVariant[notice.variant]} - {notice.text} - - ))} + {notice && ( +
+ {iconForNoticeVariant[notice.variant]} +
{notice.text}
+
+ )} Date: Thu, 20 Mar 2025 15:58:16 +0100 Subject: [PATCH 6/6] Fix css linting issues --- .../react/src/SelectPanel/SelectPanel.module.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react/src/SelectPanel/SelectPanel.module.css b/packages/react/src/SelectPanel/SelectPanel.module.css index 6bbe5b3531b..5148c6b4bef 100644 --- a/packages/react/src/SelectPanel/SelectPanel.module.css +++ b/packages/react/src/SelectPanel/SelectPanel.module.css @@ -21,17 +21,17 @@ } .Notice { - margin-top: var(--base-size-4); display: flex; - flex-direction: row; - gap: var(--base-size-8); padding-top: var(--base-size-12); padding-right: var(--base-size-16); - padding-left: var(--base-size-16); padding-bottom: var(--base-size-12); - border-top: 1px solid; - border-bottom: 1px solid; + padding-left: var(--base-size-16); + margin-top: var(--base-size-4); font-size: var(--text-body-size-small); + flex-direction: row; + border-top: var(--borderWidth-thin) solid; + border-bottom: var(--borderWidth-thin) solid; + gap: var(--base-size-8); } .Notice a {