@transferwise/components 46.126.0 → 46.128.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/build/alert/Alert.js +3 -0
  2. package/build/alert/Alert.js.map +1 -1
  3. package/build/alert/Alert.mjs +3 -0
  4. package/build/alert/Alert.mjs.map +1 -1
  5. package/build/index.js +1 -0
  6. package/build/index.js.map +1 -1
  7. package/build/index.mjs +1 -1
  8. package/build/inputs/SelectInput.js +81 -12
  9. package/build/inputs/SelectInput.js.map +1 -1
  10. package/build/inputs/SelectInput.mjs +81 -13
  11. package/build/inputs/SelectInput.mjs.map +1 -1
  12. package/build/listItem/Prompt/ListItemPrompt.js +5 -4
  13. package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
  14. package/build/listItem/Prompt/ListItemPrompt.mjs +6 -2
  15. package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
  16. package/build/main.css +36 -28
  17. package/build/prompt/ActionPrompt/ActionPrompt.js +6 -4
  18. package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
  19. package/build/prompt/ActionPrompt/ActionPrompt.mjs +6 -4
  20. package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
  21. package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
  22. package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
  23. package/build/prompt/InlinePrompt/InlinePrompt.js +1 -1
  24. package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
  25. package/build/prompt/InlinePrompt/InlinePrompt.mjs +1 -1
  26. package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
  27. package/build/styles/main.css +36 -28
  28. package/build/styles/prompt/ActionPrompt/ActionPrompt.css +4 -0
  29. package/build/styles/prompt/InfoPrompt/InfoPrompt.css +7 -5
  30. package/build/styles/prompt/InlinePrompt/InlinePrompt.css +3 -2
  31. package/build/styles/prompt/PrimitivePrompt/PrimitivePrompt.css +1 -0
  32. package/build/styles/sentimentSurface/SentimentSurface.css +21 -21
  33. package/build/types/alert/Alert.d.ts +15 -0
  34. package/build/types/alert/Alert.d.ts.map +1 -1
  35. package/build/types/index.d.ts +1 -1
  36. package/build/types/index.d.ts.map +1 -1
  37. package/build/types/inputs/SelectInput.d.ts +19 -0
  38. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  39. package/build/types/listItem/ListItem.d.ts +1 -1
  40. package/build/types/listItem/Prompt/ListItemPrompt.d.ts +2 -3
  41. package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -1
  42. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +7 -0
  43. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
  44. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +4 -2
  45. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
  46. package/package.json +3 -3
  47. package/src/accordion/Accordion.test.js +0 -6
  48. package/src/accordion/AccordionItem/AccordionItem.test.js +0 -10
  49. package/src/actionButton/ActionButton.test.tsx +0 -4
  50. package/src/alert/Alert.story.tsx +4 -0
  51. package/src/alert/Alert.test.story.tsx +1 -1
  52. package/src/alert/Alert.tsx +16 -0
  53. package/src/avatarWrapper/AvatarWrapper.test.tsx +0 -53
  54. package/src/checkbox/Checkbox.test.tsx +0 -5
  55. package/src/chevron/Chevron.test.tsx +0 -7
  56. package/src/chips/Chips.test.tsx +0 -8
  57. package/src/common/RadioButton/RadioButton.test.tsx +0 -18
  58. package/src/common/bottomSheet/BottomSheet.test.tsx +0 -9
  59. package/src/common/card/Card.test.tsx +0 -6
  60. package/src/common/closeButton/CloseButton.test.tsx +0 -4
  61. package/src/common/panel/Panel.test.tsx +0 -6
  62. package/src/flowNavigation/FlowNavigation.test.js +0 -10
  63. package/src/iconButton/IconButton.story.tsx +173 -48
  64. package/src/iconButton/IconButton.test.story.tsx +194 -0
  65. package/src/index.ts +1 -0
  66. package/src/inputs/SelectInput.story.tsx +33 -20
  67. package/src/inputs/SelectInput.test.story.tsx +1285 -5
  68. package/src/inputs/SelectInput.tsx +93 -15
  69. package/src/listItem/Prompt/ListItemPrompt.story.tsx +71 -9
  70. package/src/listItem/Prompt/ListItemPrompt.test.tsx +31 -0
  71. package/src/listItem/Prompt/ListItemPrompt.tsx +8 -2
  72. package/src/listItem/_stories/ListItem.story.tsx +0 -1
  73. package/src/logo/Logo.story.tsx +24 -5
  74. package/src/main.css +36 -28
  75. package/src/overlayHeader/OverlayHeader.test.tsx +0 -3
  76. package/src/popover/Popover.test.tsx +0 -25
  77. package/src/promoCard/PromoCard.test.tsx +0 -6
  78. package/src/promoCard/PromoCardGroup.test.tsx +0 -5
  79. package/src/prompt/ActionPrompt/ActionPrompt.accessibility.docs.mdx +2 -18
  80. package/src/prompt/ActionPrompt/ActionPrompt.css +4 -0
  81. package/src/prompt/ActionPrompt/ActionPrompt.less +5 -1
  82. package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +323 -108
  83. package/src/prompt/ActionPrompt/ActionPrompt.test.story.tsx +86 -3
  84. package/src/prompt/ActionPrompt/ActionPrompt.tsx +17 -6
  85. package/src/prompt/InfoPrompt/InfoPrompt.accessibility.docs.mdx +79 -0
  86. package/src/prompt/InfoPrompt/InfoPrompt.css +7 -5
  87. package/src/prompt/InfoPrompt/InfoPrompt.less +8 -8
  88. package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +112 -82
  89. package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +54 -1
  90. package/src/prompt/InfoPrompt/InfoPrompt.tsx +4 -2
  91. package/src/prompt/InlinePrompt/InlinePrompt.accessibility.docs.mdx +63 -0
  92. package/src/prompt/InlinePrompt/InlinePrompt.css +3 -2
  93. package/src/prompt/InlinePrompt/InlinePrompt.less +2 -2
  94. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +38 -40
  95. package/src/prompt/InlinePrompt/InlinePrompt.test.story.tsx +21 -0
  96. package/src/prompt/InlinePrompt/InlinePrompt.test.tsx +23 -4
  97. package/src/prompt/InlinePrompt/InlinePrompt.tsx +1 -1
  98. package/src/prompt/PrimitivePrompt/PrimitivePrompt.css +1 -0
  99. package/src/prompt/PrimitivePrompt/PrimitivePrompt.less +2 -1
  100. package/src/sentimentSurface/SentimentSurface.css +21 -21
  101. package/src/sentimentSurface/SentimentSurface.docs.mdx +1 -1
  102. package/src/sentimentSurface/SentimentSurface.less +13 -13
  103. package/src/sentimentSurface/SentimentSurface.test.story.tsx +47 -0
  104. package/src/tile/Tile.test.tsx +0 -10
  105. package/src/tooltip/Tooltip.test.tsx +0 -10
  106. package/src/accordion/AccordionItem/__snapshots__/AccordionItem.test.js.snap +0 -124
  107. package/src/accordion/__snapshots__/Accordion.test.js.snap +0 -3
  108. package/src/actionButton/__snapshots__/ActionButton.test.tsx.snap +0 -12
  109. package/src/avatarWrapper/__snapshots__/AvatarWrapper.test.tsx.snap +0 -156
  110. package/src/checkbox/__snapshots__/Checkbox.test.tsx.snap +0 -40
  111. package/src/chevron/__snapshots__/Chevron.test.tsx.snap +0 -24
  112. package/src/chips/__snapshots__/Chips.test.tsx.snap +0 -153
  113. package/src/common/RadioButton/__snapshots__/RadioButton.test.tsx.snap +0 -58
  114. package/src/common/bottomSheet/__snapshots__/BottomSheet.test.tsx.snap +0 -80
  115. package/src/common/card/__snapshots__/Card.test.tsx.snap +0 -10
  116. package/src/common/closeButton/__snapshots__/CloseButton.test.tsx.snap +0 -30
  117. package/src/common/flowHeader/FlowHeader.test.tsx +0 -22
  118. package/src/common/flowHeader/__snapshots__/FlowHeader.test.tsx.snap +0 -33
  119. package/src/common/panel/__snapshots__/Panel.test.tsx.snap +0 -3
  120. package/src/flowNavigation/__snapshots__/FlowNavigation.test.js.snap +0 -262
  121. package/src/logo/Logo.test.tsx +0 -55
  122. package/src/logo/__snapshots__/Logo.test.tsx.snap +0 -281
  123. package/src/overlayHeader/__snapshots__/OverlayHeader.test.tsx.snap +0 -65
  124. package/src/popover/__snapshots__/Popover.test.tsx.snap +0 -51
  125. package/src/promoCard/__snapshots__/PromoCard.test.tsx.snap +0 -40
  126. package/src/promoCard/__snapshots__/PromoCardGroup.test.tsx.snap +0 -80
  127. package/src/tile/__snapshots__/Tile.test.tsx.snap +0 -55
  128. package/src/tooltip/__snapshots__/Tooltip.test.tsx.snap +0 -32
@@ -1,5 +1,18 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { ArrowLeft, Cross, Defrost, Edit, Menu, Plus } from '@transferwise/icons';
2
+ import {
3
+ ArrowLeft,
4
+ Cross,
5
+ Defrost,
6
+ Edit,
7
+ Menu,
8
+ Plus,
9
+ Settings,
10
+ Star,
11
+ Travel,
12
+ Briefcase,
13
+ Bank,
14
+ Freeze,
15
+ } from '@transferwise/icons';
3
16
  import IconButton, { Props } from './IconButton';
4
17
  import { action } from 'storybook/actions';
5
18
  import Body from '../body';
@@ -114,48 +127,167 @@ export const Basic: Story = {
114
127
  */
115
128
  export const SentimentAwareness: Story = {
116
129
  render: () => {
130
+ const priorities = ['primary', 'secondary', 'tertiary', 'minimal', 'disabled'] as const;
131
+ const sentiments = ['success', 'warning', 'negative', 'neutral', 'proposition'] as const;
132
+
117
133
  return (
118
- <>
119
- {(['success', 'warning', 'negative', 'neutral', 'proposition'] as const).map(
120
- (sentiment) => (
121
- <SentimentSurface
122
- key={sentiment}
123
- sentiment={sentiment}
124
- className="p-a-1 d-flex align-items-center"
125
- style={{ gap: 'var(--size-8)' }}
134
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
135
+ {/* Header row with priority labels */}
136
+ <div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '4px', gap: '8px' }}>
137
+ <div style={{ width: '82px', paddingLeft: '8px' }} />
138
+ {priorities.map((priority) => (
139
+ <div
140
+ key={priority}
141
+ style={{
142
+ width: '32px',
143
+ textAlign: 'center',
144
+ fontSize: '11px',
145
+ fontWeight: 'bold',
146
+ writingMode: 'vertical-rl',
147
+ transform: 'rotate(180deg)',
148
+ height: '60px',
149
+ display: 'flex',
150
+ alignItems: 'center',
151
+ justifyContent: 'center',
152
+ }}
126
153
  >
127
- <IconButton
128
- size={32}
129
- aria-label="Primary action"
130
- priority="primary"
131
- type="default"
132
- onClick={action('button click')}
133
- >
134
- <Plus />
135
- </IconButton>
136
- <IconButton
137
- size={32}
138
- aria-label="Secondary action"
139
- priority="secondary"
140
- type="default"
141
- onClick={action('button click')}
142
- >
143
- <Defrost />
144
- </IconButton>
145
- <IconButton
146
- size={32}
147
- aria-label="Disabled action"
148
- priority="primary"
149
- type="default"
150
- disabled
151
- onClick={action('button click')}
152
- >
153
- <Menu />
154
- </IconButton>
155
- </SentimentSurface>
156
- ),
157
- )}
158
- </>
154
+ {priority}
155
+ </div>
156
+ ))}
157
+ </div>
158
+
159
+ {/* Rows for each sentiment with base and elevated emphasis */}
160
+ {sentiments.flatMap((sentiment) => [
161
+ <SentimentSurface
162
+ key={`${sentiment}-base`}
163
+ sentiment={sentiment}
164
+ emphasis="base"
165
+ style={{ display: 'flex', alignItems: 'center', padding: '8px 0', gap: '8px' }}
166
+ >
167
+ <div
168
+ style={{
169
+ width: '82px',
170
+ fontSize: '11px',
171
+ fontWeight: 'bold',
172
+ textAlign: 'left',
173
+ paddingLeft: '8px',
174
+ }}
175
+ >
176
+ {sentiment} (base)
177
+ </div>
178
+ <IconButton
179
+ size={32}
180
+ aria-label="Primary action"
181
+ priority="primary"
182
+ type="default"
183
+ onClick={action('button click')}
184
+ >
185
+ <Plus />
186
+ </IconButton>
187
+ <IconButton
188
+ size={32}
189
+ aria-label="Secondary action"
190
+ priority="secondary"
191
+ type="default"
192
+ onClick={action('button click')}
193
+ >
194
+ <Settings />
195
+ </IconButton>
196
+ <IconButton
197
+ size={32}
198
+ aria-label="Tertiary action"
199
+ priority="tertiary"
200
+ type="default"
201
+ onClick={action('button click')}
202
+ >
203
+ <Star />
204
+ </IconButton>
205
+ <IconButton
206
+ size={32}
207
+ aria-label="Minimal action"
208
+ priority="minimal"
209
+ type="default"
210
+ onClick={action('button click')}
211
+ >
212
+ <Travel />
213
+ </IconButton>
214
+ <IconButton
215
+ size={32}
216
+ aria-label="Disabled action"
217
+ priority="primary"
218
+ type="default"
219
+ disabled
220
+ onClick={action('button click')}
221
+ >
222
+ <Menu />
223
+ </IconButton>
224
+ </SentimentSurface>,
225
+ <SentimentSurface
226
+ key={`${sentiment}-elevated`}
227
+ sentiment={sentiment}
228
+ emphasis="elevated"
229
+ style={{ display: 'flex', alignItems: 'center', padding: '8px 0', gap: '8px' }}
230
+ >
231
+ <div
232
+ style={{
233
+ width: '82px',
234
+ fontSize: '11px',
235
+ fontWeight: 'bold',
236
+ textAlign: 'left',
237
+ paddingLeft: '8px',
238
+ }}
239
+ >
240
+ {sentiment} (elevated)
241
+ </div>
242
+ <IconButton
243
+ size={32}
244
+ aria-label="Primary action"
245
+ priority="primary"
246
+ type="default"
247
+ onClick={action('button click')}
248
+ >
249
+ <Briefcase />
250
+ </IconButton>
251
+ <IconButton
252
+ size={32}
253
+ aria-label="Secondary action"
254
+ priority="secondary"
255
+ type="default"
256
+ onClick={action('button click')}
257
+ >
258
+ <Bank />
259
+ </IconButton>
260
+ <IconButton
261
+ size={32}
262
+ aria-label="Tertiary action"
263
+ priority="tertiary"
264
+ type="default"
265
+ onClick={action('button click')}
266
+ >
267
+ <Freeze />
268
+ </IconButton>
269
+ <IconButton
270
+ size={32}
271
+ aria-label="Minimal action"
272
+ priority="minimal"
273
+ type="default"
274
+ onClick={action('button click')}
275
+ >
276
+ <Edit />
277
+ </IconButton>
278
+ <IconButton
279
+ size={32}
280
+ aria-label="Disabled action"
281
+ priority="primary"
282
+ type="default"
283
+ disabled
284
+ onClick={action('button click')}
285
+ >
286
+ <Cross />
287
+ </IconButton>
288
+ </SentimentSurface>,
289
+ ])}
290
+ </div>
159
291
  );
160
292
  },
161
293
  parameters: {
@@ -166,11 +298,4 @@ export const SentimentAwareness: Story = {
166
298
  },
167
299
  },
168
300
  },
169
- decorators: [
170
- (Story) => (
171
- <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
172
- <Story />
173
- </div>
174
- ),
175
- ],
176
301
  };
@@ -0,0 +1,194 @@
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { Menu, Plus, Settings, Star, Travel } from '@transferwise/icons';
3
+ import { action } from 'storybook/actions';
4
+ import IconButton from './IconButton';
5
+ import SentimentSurface from '../sentimentSurface';
6
+ import { allModes } from '../../.storybook/modes';
7
+
8
+ export default {
9
+ title: 'Actions/IconButton/Tests',
10
+ component: IconButton,
11
+ tags: ['!autodocs', '!manifest'],
12
+ } satisfies Meta<typeof IconButton>;
13
+
14
+ type Story = StoryObj<typeof IconButton>;
15
+
16
+ /**
17
+ * IconButton displayed across all themes and sentiments for visual regression testing.
18
+ */
19
+ export const AllThemesAndSentiments: Story = {
20
+ render: () => {
21
+ const priorities = ['primary', 'secondary', 'tertiary', 'minimal', 'disabled'] as const;
22
+ const sentiments = ['negative', 'warning', 'neutral', 'success', 'proposition'] as const;
23
+ const icons = [Plus, Settings, Star, Travel];
24
+
25
+ return (
26
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
27
+ {/* Header row with priority labels */}
28
+ <div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '4px', gap: '8px' }}>
29
+ <div style={{ width: '82px', paddingLeft: '8px' }} />
30
+ {priorities.map((priority) => (
31
+ <div
32
+ key={priority}
33
+ style={{
34
+ width: '32px',
35
+ textAlign: 'center',
36
+ fontSize: '11px',
37
+ fontWeight: 'bold',
38
+ writingMode: 'vertical-rl',
39
+ transform: 'rotate(180deg)',
40
+ height: '60px',
41
+ display: 'flex',
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ }}
45
+ >
46
+ {priority}
47
+ </div>
48
+ ))}
49
+ </div>
50
+
51
+ {/* Rows for each sentiment with base and elevated emphasis */}
52
+ {sentiments.flatMap((sentiment) => [
53
+ <SentimentSurface
54
+ key={`${sentiment}-base`}
55
+ sentiment={sentiment}
56
+ emphasis="base"
57
+ style={{ display: 'flex', alignItems: 'center', padding: '8px 0', gap: '8px' }}
58
+ >
59
+ <div
60
+ style={{
61
+ width: '82px',
62
+ fontSize: '11px',
63
+ fontWeight: 'bold',
64
+ textAlign: 'left',
65
+ paddingLeft: '8px',
66
+ }}
67
+ >
68
+ {sentiment} (base)
69
+ </div>
70
+ <IconButton
71
+ size={32}
72
+ aria-label="Primary action"
73
+ priority="primary"
74
+ type="default"
75
+ onClick={action('button click')}
76
+ >
77
+ <Plus />
78
+ </IconButton>
79
+ <IconButton
80
+ size={32}
81
+ aria-label="Secondary action"
82
+ priority="secondary"
83
+ type="default"
84
+ onClick={action('button click')}
85
+ >
86
+ <Settings />
87
+ </IconButton>
88
+ <IconButton
89
+ size={32}
90
+ aria-label="Tertiary action"
91
+ priority="tertiary"
92
+ type="default"
93
+ onClick={action('button click')}
94
+ >
95
+ <Star />
96
+ </IconButton>
97
+ <IconButton
98
+ size={32}
99
+ aria-label="Minimal action"
100
+ priority="minimal"
101
+ type="default"
102
+ onClick={action('button click')}
103
+ >
104
+ <Travel />
105
+ </IconButton>
106
+ <IconButton
107
+ size={32}
108
+ aria-label="Disabled action"
109
+ priority="primary"
110
+ type="default"
111
+ disabled
112
+ onClick={action('button click')}
113
+ >
114
+ <Menu />
115
+ </IconButton>
116
+ </SentimentSurface>,
117
+ <SentimentSurface
118
+ key={`${sentiment}-elevated`}
119
+ sentiment={sentiment}
120
+ emphasis="elevated"
121
+ style={{ display: 'flex', alignItems: 'center', padding: '8px 0', gap: '8px' }}
122
+ >
123
+ <div
124
+ style={{
125
+ width: '82px',
126
+ fontSize: '11px',
127
+ fontWeight: 'bold',
128
+ textAlign: 'left',
129
+ paddingLeft: '8px',
130
+ }}
131
+ >
132
+ {sentiment} (elevated)
133
+ </div>
134
+ <IconButton
135
+ size={32}
136
+ aria-label="Primary action"
137
+ priority="primary"
138
+ type="default"
139
+ onClick={action('button click')}
140
+ >
141
+ <Plus />
142
+ </IconButton>
143
+ <IconButton
144
+ size={32}
145
+ aria-label="Secondary action"
146
+ priority="secondary"
147
+ type="default"
148
+ onClick={action('button click')}
149
+ >
150
+ <Settings />
151
+ </IconButton>
152
+ <IconButton
153
+ size={32}
154
+ aria-label="Tertiary action"
155
+ priority="tertiary"
156
+ type="default"
157
+ onClick={action('button click')}
158
+ >
159
+ <Star />
160
+ </IconButton>
161
+ <IconButton
162
+ size={32}
163
+ aria-label="Minimal action"
164
+ priority="minimal"
165
+ type="default"
166
+ onClick={action('button click')}
167
+ >
168
+ <Travel />
169
+ </IconButton>
170
+ <IconButton
171
+ size={32}
172
+ aria-label="Disabled action"
173
+ priority="primary"
174
+ type="default"
175
+ disabled
176
+ onClick={action('button click')}
177
+ >
178
+ <Menu />
179
+ </IconButton>
180
+ </SentimentSurface>,
181
+ ])}
182
+ </div>
183
+ );
184
+ },
185
+ parameters: {
186
+ padding: '16px',
187
+ variants: ['default', 'dark', 'bright-green', 'forest-green'],
188
+ chromatic: {
189
+ dark: allModes.dark,
190
+ brightGreen: allModes.brightGreen,
191
+ forestGreen: allModes.forestGreen,
192
+ },
193
+ },
194
+ };
package/src/index.ts CHANGED
@@ -181,6 +181,7 @@ export {
181
181
  SelectInput,
182
182
  SelectInputOptionContent,
183
183
  SelectInputTriggerButton,
184
+ sortByRelevance,
184
185
  } from './inputs/SelectInput';
185
186
  export { TextArea } from './inputs/TextArea';
186
187
  export { default as InstructionsList } from './instructionsList';
@@ -624,6 +624,7 @@ interface CountryWithCurrency extends Country {
624
624
  const countriesWithCurrency: CountryWithCurrency[] = [
625
625
  { code: 'AD', name: 'Andorra', keywords: ['united states dollar'] },
626
626
  { code: 'DE', name: 'Germany', keywords: ['EUR'] },
627
+ { code: 'NR', name: 'New United Republic', keywords: ['NUR'] },
627
628
  { code: 'US', name: 'United States', keywords: ['USD'] },
628
629
  { code: 'ZM', name: 'Zambia', keywords: ['USD', 'united states dollar'] },
629
630
  ];
@@ -649,38 +650,50 @@ export const WithCustomSearchResultSorting: Story<CountryWithCurrency> = {
649
650
  value: countriesWithCurrency[0],
650
651
  filterable: true,
651
652
  filterPlaceholder: 'Type a currency / country',
652
- sortFilteredOptions: (a, b, searchQuery) => {
653
- const normalizedQuery = searchQuery.toLowerCase();
654
- const nameA = a.value.name.toLowerCase();
655
- const nameB = b.value.name.toLowerCase();
656
-
657
- // Prioritize countries where name contains the search query
658
- const aNameMatch = nameA.includes(normalizedQuery);
659
- const bNameMatch = nameB.includes(normalizedQuery);
660
-
661
- if (aNameMatch && !bNameMatch) return -1;
662
- if (!aNameMatch && bNameMatch) return 1;
663
-
664
- // Then sort alphabetically
665
- return nameA.localeCompare(nameB);
666
- },
653
+ sortFilteredOptions: SelectInput.sortByRelevance((value) => value.name),
667
654
  size: 'lg',
668
655
  } satisfies Story<CountryWithCurrency>['args'],
669
656
  decorators: [
670
657
  (Story) => (
671
658
  <div>
672
659
  <p className="m-b-3 np-text-body-default" style={{ maxWidth: '600px' }}>
673
- This example demonstrates custom sorting with the <code>sortFilteredOptions</code> prop.
660
+ This example uses the built-in <code>SelectInput.sortByRelevance</code> helper to sort
661
+ filtered results by relevance. It prioritises: exact matches → starts with → contains →
662
+ alphabetical.
674
663
  <br />
675
664
  <br />
676
- Try searching for &quot;united&quot; - Without the custom sorter, it would just be the
677
- provided options order.
665
+ Try searching for &quot;united&quot; to see the sorting tiers in action:
678
666
  <br />
679
- With a customer sorter function in this story, the countries with &quot;United&quot; in
680
- their name appear first, followed by countries that only match via keywords.
667
+ 1. <strong>United States</strong> name starts with &quot;United&quot;
668
+ <br />
669
+ 2. <strong>New United Republic</strong> — name contains &quot;United&quot;
670
+ <br />
671
+ 3. <strong>Andorra, Zambia</strong> — match only via keywords
681
672
  </p>
682
673
  <Story />
683
674
  </div>
684
675
  ),
685
676
  ],
677
+ parameters: {
678
+ docs: {
679
+ source: {
680
+ code: `<SelectInput
681
+ filterable
682
+ filterPlaceholder="Type a currency / country"
683
+ sortFilteredOptions={SelectInput.sortByRelevance((value) => value.name)}
684
+ items={countries.map((country) => ({
685
+ type: 'option',
686
+ value: country,
687
+ filterMatchers: [country.name, country.code, ...country.keywords],
688
+ }))}
689
+ renderValue={(country) => (
690
+ <SelectInputOptionContent
691
+ title={country.name}
692
+ icon={<Flag code={country.code} />}
693
+ />
694
+ )}
695
+ />`,
696
+ },
697
+ },
698
+ },
686
699
  };