Skip to content

Commit 56248f7

Browse files
feat(SelectPanel): display selected items at the top under FF (#5971)
1 parent 1925d80 commit 56248f7

File tree

63 files changed

+238
-191
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+238
-191
lines changed

.changeset/shy-falcons-shout.md

Lines changed: 5 additions & 0 deletions

packages/react/src/FeatureFlags/DefaultFeatureFlags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({
77
primer_react_overlay_overflow: false,
88
primer_react_segmented_control_tooltip: false,
99
primer_react_select_panel_fullscreen_on_narrow: false,
10+
primer_react_select_panel_order_selected_at_top: true,
1011
})

packages/react/src/SelectPanel/SelectPanel.docs.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@
177177
"type": "React.ReactElement",
178178
"defaultValue": "null",
179179
"description": "Secondary action, it will be rendered in the footer of the panel. Use `SecondaryActionButton` or `SecondaryActionLink` for the action."
180+
},
181+
{
182+
"name": "showSelectedOptionsFirst",
183+
"type": "boolean",
184+
"description": "Whether to display the selected items at the top of the list",
185+
"default": "true"
180186
}
181187
],
182188
"subcomponents": []

packages/react/src/SelectPanel/SelectPanel.examples.stories.tsx

Lines changed: 24 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,7 @@ export const HeightInitialWithOverflowingItemsStory = () => {
5757
const [selected, setSelected] = useState<ItemInput[]>(items.slice(1, 3))
5858
const [filter, setFilter] = useState('')
5959
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
60-
// design guidelines say to sort selected items first
61-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
62-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
63-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
64-
if (aIsSelected && !bIsSelected) return -1
65-
if (!aIsSelected && bIsSelected) return 1
66-
return 0
67-
})
60+
6861
const [open, setOpen] = useState(false)
6962

7063
return (
@@ -79,12 +72,12 @@ export const HeightInitialWithOverflowingItemsStory = () => {
7972
placeholder="Select labels" // button text when no items are selected
8073
open={open}
8174
onOpenChange={setOpen}
82-
items={selectedItemsSortedFirst}
75+
items={filteredItems}
8376
selected={selected}
8477
onSelectedChange={setSelected}
8578
onFilterChange={setFilter}
8679
overlayProps={{width: 'small', height: 'initial', maxHeight: 'xsmall'}}
87-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
80+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
8881
/>
8982
</FormControl>
9083
)
@@ -96,14 +89,7 @@ export const HeightInitialWithUnderflowingItemsStory = () => {
9689
const [selected, setSelected] = useState<ItemInput[]>([underflowingItems[0]])
9790
const [filter, setFilter] = useState('')
9891
const filteredItems = underflowingItems.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
99-
// design guidelines say to sort selected items first
100-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
101-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
102-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
103-
if (aIsSelected && !bIsSelected) return -1
104-
if (!aIsSelected && bIsSelected) return 1
105-
return 0
106-
})
92+
10793
const [open, setOpen] = useState(false)
10894

10995
return (
@@ -118,13 +104,13 @@ export const HeightInitialWithUnderflowingItemsStory = () => {
118104
placeholder="Select labels" // button text when no items are selected
119105
open={open}
120106
onOpenChange={setOpen}
121-
items={selectedItemsSortedFirst}
107+
items={filteredItems}
122108
selected={selected}
123109
onSelectedChange={setSelected}
124110
onFilterChange={setFilter}
125111
showItemDividers={true}
126112
overlayProps={{width: 'small', height: 'initial', maxHeight: 'xsmall'}}
127-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
113+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
128114
/>
129115
</FormControl>
130116
)
@@ -139,14 +125,7 @@ export const HeightInitialWithUnderflowingItemsAfterFetch = () => {
139125
() => fetchedItems.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase())),
140126
[fetchedItems, filter],
141127
)
142-
// design guidelines say to sort selected items first
143-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
144-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
145-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
146-
if (aIsSelected && !bIsSelected) return -1
147-
if (!aIsSelected && bIsSelected) return 1
148-
return 0
149-
})
128+
150129
const [open, setOpen] = useState(false)
151130
const [height, setHeight] = useState<OverlayProps['height']>('auto')
152131

@@ -171,13 +150,13 @@ export const HeightInitialWithUnderflowingItemsAfterFetch = () => {
171150
open={open}
172151
onOpenChange={onOpenChange}
173152
loading={filteredItems.length === 0 && !filter}
174-
items={selectedItemsSortedFirst}
153+
items={filteredItems}
175154
selected={selected}
176155
onSelectedChange={setSelected}
177156
onFilterChange={setFilter}
178157
showItemDividers={true}
179158
overlayProps={{width: 'small', height, maxHeight: 'xsmall'}}
180-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
159+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
181160
/>
182161
</FormControl>
183162
)
@@ -188,14 +167,7 @@ export const AboveTallBody = () => {
188167
const [selected, setSelected] = useState<ItemInput[]>(items.slice(1, 3))
189168
const [filter, setFilter] = useState('')
190169
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
191-
// design guidelines say to sort selected items first
192-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
193-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
194-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
195-
if (aIsSelected && !bIsSelected) return -1
196-
if (!aIsSelected && bIsSelected) return 1
197-
return 0
198-
})
170+
199171
const [open, setOpen] = useState(false)
200172
return (
201173
<FormControl>
@@ -209,12 +181,12 @@ export const AboveTallBody = () => {
209181
placeholder="Select labels" // button text when no items are selected
210182
open={open}
211183
onOpenChange={setOpen}
212-
items={selectedItemsSortedFirst}
184+
items={filteredItems}
213185
selected={selected}
214186
onSelectedChange={setSelected}
215187
onFilterChange={setFilter}
216188
showItemDividers={true}
217-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
189+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
218190
/>
219191
<div
220192
style={{
@@ -238,23 +210,13 @@ export const HeightVariationsAndScroll = () => {
238210
// Example A
239211
const [selectedA, setSelectedA] = React.useState<ItemInput | undefined>(longItems[0])
240212
const filteredItemsA = longItems.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
241-
// design guidelines say to sort selected items first
242-
const selectedItemsSortedFirstA = filteredItemsA.sort((a, b) => {
243-
if (a.text === selectedA?.text) return -1
244-
if (b.text === selectedA?.text) return 1
245-
return 0
246-
})
213+
247214
const [openA, setOpenA] = useState(false)
248215

249216
// Example B
250217
const [selectedB, setSelectedB] = React.useState<ItemInput | undefined>(longItems[0])
251218
const filteredItemsB = longItems.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
252-
// design guidelines say to sort selected items first
253-
const selectedItemsSortedFirstB = filteredItemsB.sort((a, b) => {
254-
if (a.text === selectedB?.text) return -1
255-
if (b.text === selectedB?.text) return 1
256-
return 0
257-
})
219+
258220
const [openB, setOpenB] = useState(false)
259221

260222
return (
@@ -270,13 +232,13 @@ export const HeightVariationsAndScroll = () => {
270232
placeholder="Select labels" // button text when no items are selected
271233
open={openA}
272234
onOpenChange={setOpenA}
273-
items={selectedItemsSortedFirstA}
235+
items={filteredItemsA}
274236
selected={selectedA}
275237
onSelectedChange={setSelectedA}
276238
onFilterChange={setFilter}
277239
showItemDividers={true}
278240
overlayProps={{height: 'medium'}}
279-
message={selectedItemsSortedFirstA.length === 0 ? NoResultsMessage(filter) : undefined}
241+
message={filteredItemsA.length === 0 ? NoResultsMessage(filter) : undefined}
280242
/>
281243
</FormControl>
282244
<br />
@@ -291,7 +253,7 @@ export const HeightVariationsAndScroll = () => {
291253
placeholder="Select labels" // button text when no items are selected
292254
open={openB}
293255
onOpenChange={setOpenB}
294-
items={selectedItemsSortedFirstB}
256+
items={filteredItemsB}
295257
selected={selectedB}
296258
onSelectedChange={setSelectedB}
297259
onFilterChange={setFilter}
@@ -300,7 +262,7 @@ export const HeightVariationsAndScroll = () => {
300262
height: 'auto',
301263
maxHeight: 'medium',
302264
}}
303-
message={selectedItemsSortedFirstB.length === 0 ? NoResultsMessage(filter) : undefined}
265+
message={filteredItemsB.length === 0 ? NoResultsMessage(filter) : undefined}
304266
/>
305267
</FormControl>
306268
</>
@@ -317,14 +279,7 @@ export const CustomItemRenderer = () => {
317279
const [selected, setSelected] = useState<ItemInput[]>(items.slice(1, 3))
318280
const [filter, setFilter] = useState('')
319281
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
320-
// design guidelines say to sort selected items first
321-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
322-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
323-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
324-
if (aIsSelected && !bIsSelected) return -1
325-
if (!aIsSelected && bIsSelected) return 1
326-
return 0
327-
})
282+
328283
const [open, setOpen] = useState(false)
329284

330285
return (
@@ -339,7 +294,7 @@ export const CustomItemRenderer = () => {
339294
)}
340295
open={open}
341296
onOpenChange={setOpen}
342-
items={selectedItemsSortedFirst}
297+
items={filteredItems}
343298
selected={selected}
344299
onSelectedChange={setSelected}
345300
onFilterChange={setFilter}
@@ -369,7 +324,7 @@ export const CustomItemRenderer = () => {
369324
</Box>
370325
</ActionList.Item>
371326
)}
372-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
327+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
373328
/>
374329
</FormControl>
375330
)
@@ -392,14 +347,7 @@ export const ItemsInScope = () => {
392347
const [selected, setSelected] = useState<ItemInput[]>(items.slice(1, 3))
393348
const [filter, setFilter] = useState('')
394349
const filteredItems = items.filter(item => item.text.toLowerCase().startsWith(filter.toLowerCase()))
395-
// design guidelines say to sort selected items first
396-
const selectedItemsSortedFirst = filteredItems.sort((a, b) => {
397-
const aIsSelected = selected.some(selectedItem => selectedItem.text === a.text)
398-
const bIsSelected = selected.some(selectedItem => selectedItem.text === b.text)
399-
if (aIsSelected && !bIsSelected) return -1
400-
if (!aIsSelected && bIsSelected) return 1
401-
return 0
402-
})
350+
403351
const [open, setOpen] = useState(false)
404352
return (
405353
<FormControl>
@@ -409,11 +357,11 @@ export const ItemsInScope = () => {
409357
placeholder="Select labels" // button text when no items are selected
410358
open={open}
411359
onOpenChange={setOpen}
412-
items={selectedItemsSortedFirst}
360+
items={filteredItems}
413361
selected={selected}
414362
onSelectedChange={setSelected}
415363
onFilterChange={setFilter}
416-
message={selectedItemsSortedFirst.length === 0 ? NoResultsMessage(filter) : undefined}
364+
message={filteredItems.length === 0 ? NoResultsMessage(filter) : undefined}
417365
/>
418366
</FormControl>
419367
)

0 commit comments

Comments
 (0)