@transferwise/components 0.0.0-experimental-c8d90fc → 0.0.0-experimental-2118b34

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 (74) hide show
  1. package/build/alert/Alert.js +1 -1
  2. package/build/alert/Alert.js.map +1 -1
  3. package/build/alert/Alert.mjs +1 -1
  4. package/build/alert/Alert.mjs.map +1 -1
  5. package/build/chips/Chip.js +3 -11
  6. package/build/chips/Chip.js.map +1 -1
  7. package/build/chips/Chip.mjs +3 -11
  8. package/build/chips/Chip.mjs.map +1 -1
  9. package/build/field/Field.js +6 -3
  10. package/build/field/Field.js.map +1 -1
  11. package/build/field/Field.mjs +6 -3
  12. package/build/field/Field.mjs.map +1 -1
  13. package/build/inlineAlert/InlineAlert.js +1 -7
  14. package/build/inlineAlert/InlineAlert.js.map +1 -1
  15. package/build/inlineAlert/InlineAlert.mjs +1 -7
  16. package/build/inlineAlert/InlineAlert.mjs.map +1 -1
  17. package/build/main.css +9 -1
  18. package/build/progressBar/ProgressBar.js +1 -5
  19. package/build/progressBar/ProgressBar.js.map +1 -1
  20. package/build/progressBar/ProgressBar.mjs +1 -5
  21. package/build/progressBar/ProgressBar.mjs.map +1 -1
  22. package/build/select/Select.js +1 -7
  23. package/build/select/Select.js.map +1 -1
  24. package/build/select/Select.mjs +1 -7
  25. package/build/select/Select.mjs.map +1 -1
  26. package/build/styles/field/Field.css +5 -1
  27. package/build/styles/main.css +9 -1
  28. package/build/styles/typeahead/Typeahead.css +4 -0
  29. package/build/typeahead/Typeahead.js +18 -6
  30. package/build/typeahead/Typeahead.js.map +1 -1
  31. package/build/typeahead/Typeahead.mjs +18 -6
  32. package/build/typeahead/Typeahead.mjs.map +1 -1
  33. package/build/types/alert/Alert.d.ts +1 -1
  34. package/build/types/alert/Alert.d.ts.map +1 -1
  35. package/build/types/chips/Chip.d.ts.map +1 -1
  36. package/build/types/field/Field.d.ts +8 -4
  37. package/build/types/field/Field.d.ts.map +1 -1
  38. package/build/types/inlineAlert/InlineAlert.d.ts +1 -7
  39. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  40. package/build/types/progressBar/ProgressBar.d.ts.map +1 -1
  41. package/build/types/select/Select.d.ts.map +1 -1
  42. package/build/types/typeahead/Typeahead.d.ts +4 -4
  43. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  44. package/build/types/upload/Upload.d.ts +1 -1
  45. package/build/upload/Upload.js.map +1 -1
  46. package/build/upload/Upload.mjs.map +1 -1
  47. package/build/upload/steps/uploadImageStep/uploadImageStep.js +3 -3
  48. package/build/upload/steps/uploadImageStep/uploadImageStep.js.map +1 -1
  49. package/build/upload/steps/uploadImageStep/uploadImageStep.mjs +3 -3
  50. package/build/upload/steps/uploadImageStep/uploadImageStep.mjs.map +1 -1
  51. package/package.json +3 -3
  52. package/src/alert/Alert.spec.tsx +1 -1
  53. package/src/alert/Alert.tsx +2 -2
  54. package/src/chips/Chip.tsx +5 -14
  55. package/src/chips/__snapshots__/Chips.spec.tsx.snap +31 -29
  56. package/src/field/Field.css +5 -1
  57. package/src/field/Field.less +7 -2
  58. package/src/field/Field.spec.tsx +19 -3
  59. package/src/field/Field.story.tsx +18 -0
  60. package/src/field/Field.tsx +16 -5
  61. package/src/inlineAlert/InlineAlert.story.tsx +4 -0
  62. package/src/inlineAlert/InlineAlert.tsx +1 -7
  63. package/src/main.css +9 -1
  64. package/src/progressBar/ProgressBar.tsx +1 -6
  65. package/src/select/Select.tsx +1 -2
  66. package/src/typeahead/Typeahead.css +4 -0
  67. package/src/typeahead/Typeahead.less +5 -1
  68. package/src/typeahead/Typeahead.spec.tsx +1 -1
  69. package/src/typeahead/Typeahead.story.tsx +80 -3
  70. package/src/typeahead/Typeahead.tsx +28 -8
  71. package/src/upload/Upload.story.tsx +1 -1
  72. package/src/upload/Upload.tests.story.tsx +36 -1
  73. package/src/upload/Upload.tsx +1 -1
  74. package/src/upload/steps/uploadImageStep/uploadImageStep.tsx +3 -3
@@ -13,12 +13,12 @@ exports[`Chips Choice Chips renders as expected 1`] = `
13
13
  role="radio"
14
14
  tabindex="0"
15
15
  >
16
- <span
16
+ <div
17
17
  aria-hidden="false"
18
- class="np-chip-label"
18
+ class="np-text-body-default-bold"
19
19
  >
20
20
  Accounting
21
- </span>
21
+ </div>
22
22
  </div>
23
23
  <div
24
24
  aria-checked="true"
@@ -26,12 +26,12 @@ exports[`Chips Choice Chips renders as expected 1`] = `
26
26
  role="radio"
27
27
  tabindex="0"
28
28
  >
29
- <span
29
+ <div
30
30
  aria-hidden="false"
31
- class="np-chip-label"
31
+ class="np-text-body-default-bold"
32
32
  >
33
33
  Payroll
34
- </span>
34
+ </div>
35
35
  </div>
36
36
  <div
37
37
  aria-checked="false"
@@ -39,12 +39,12 @@ exports[`Chips Choice Chips renders as expected 1`] = `
39
39
  role="radio"
40
40
  tabindex="0"
41
41
  >
42
- <span
42
+ <div
43
43
  aria-hidden="false"
44
- class="np-chip-label"
44
+ class="np-text-body-default-bold"
45
45
  >
46
46
  Reporting
47
- </span>
47
+ </div>
48
48
  </div>
49
49
  <div
50
50
  aria-checked="false"
@@ -52,12 +52,12 @@ exports[`Chips Choice Chips renders as expected 1`] = `
52
52
  role="radio"
53
53
  tabindex="0"
54
54
  >
55
- <span
55
+ <div
56
56
  aria-hidden="false"
57
- class="np-chip-label"
57
+ class="np-text-body-default-bold"
58
58
  >
59
59
  Payments
60
- </span>
60
+ </div>
61
61
  </div>
62
62
  </div>
63
63
  </div>
@@ -76,12 +76,12 @@ exports[`Chips Filter Chips renders as expected 1`] = `
76
76
  role="checkbox"
77
77
  tabindex="0"
78
78
  >
79
- <span
79
+ <div
80
80
  aria-hidden="false"
81
- class="np-chip-label"
81
+ class="np-text-body-default-bold"
82
82
  >
83
83
  Accounting
84
- </span>
84
+ </div>
85
85
  </div>
86
86
  <div
87
87
  aria-checked="false"
@@ -89,12 +89,12 @@ exports[`Chips Filter Chips renders as expected 1`] = `
89
89
  role="checkbox"
90
90
  tabindex="0"
91
91
  >
92
- <span
92
+ <div
93
93
  aria-hidden="false"
94
- class="np-chip-label"
94
+ class="np-text-body-default-bold"
95
95
  >
96
96
  Payroll
97
- </span>
97
+ </div>
98
98
  </div>
99
99
  <div
100
100
  aria-checked="false"
@@ -102,12 +102,12 @@ exports[`Chips Filter Chips renders as expected 1`] = `
102
102
  role="checkbox"
103
103
  tabindex="0"
104
104
  >
105
- <span
105
+ <div
106
106
  aria-hidden="false"
107
- class="np-chip-label"
107
+ class="np-text-body-default-bold"
108
108
  >
109
109
  Reporting
110
- </span>
110
+ </div>
111
111
  </div>
112
112
  <div
113
113
  aria-checked="true"
@@ -115,20 +115,20 @@ exports[`Chips Filter Chips renders as expected 1`] = `
115
115
  role="checkbox"
116
116
  tabindex="-1"
117
117
  >
118
- <span
119
- aria-hidden="false"
120
- class="np-chip-label"
118
+ <div
119
+ aria-hidden="true"
120
+ class="np-text-body-default-bold"
121
121
  >
122
122
  Payments
123
- </span>
123
+ </div>
124
124
  <button
125
125
  aria-label="Clear Payments"
126
- class="np-close-button close btn-link text-no-decoration btn-unstyled m-l-1"
126
+ class="np-close-button close btn-link text-no-decoration btn-unstyled"
127
127
  type="button"
128
128
  >
129
129
  <span
130
- class="tw-icon tw-icon-cross "
131
- data-testid="cross-icon"
130
+ class="tw-icon tw-icon-cross-circle-fill "
131
+ data-testid="cross-circle-fill-icon"
132
132
  >
133
133
  <svg
134
134
  aria-hidden="true"
@@ -140,7 +140,9 @@ exports[`Chips Filter Chips renders as expected 1`] = `
140
140
  width="16"
141
141
  >
142
142
  <path
143
- d="m12 13.414-7.293 7.293-1.414-1.414L10.586 12 3.293 4.707l1.414-1.414L12 10.586l7.293-7.293 1.414 1.414L13.414 12l7.293 7.293-1.414 1.414z"
143
+ clip-rule="evenodd"
144
+ d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10m-4.707-6.707L10.586 12 7.293 8.707l1.414-1.414L12 10.586l3.293-3.293 1.414 1.414L13.414 12l3.293 3.293-1.414 1.414L12 13.414l-3.293 3.293z"
145
+ fill-rule="evenodd"
144
146
  />
145
147
  </svg>
146
148
  </span>
@@ -1,4 +1,8 @@
1
- .np-field-control {
1
+ .np-field-control,
2
+ .np-field__prompt {
2
3
  margin-top: 4px;
3
4
  margin-top: var(--size-4);
4
5
  }
6
+ .np-field:has(.np-checkbox) label {
7
+ margin-bottom: 0;
8
+ }
@@ -1,5 +1,10 @@
1
1
  .np-field {
2
- &-control {
3
- margin-top: var(--size-4);
2
+ &-control,
3
+ &__prompt {
4
+ margin-top: var(--size-4);
5
+ }
6
+
7
+ &:has(.np-checkbox) label {
8
+ margin-bottom: 0;
4
9
  }
5
10
  }
@@ -71,7 +71,7 @@ describe('Field', () => {
71
71
  expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
72
72
  });
73
73
 
74
- it("should allow for InlineAlert's StatusIcon override via `messageIconLabel` prop", () => {
74
+ it("should allow for InlinePrompt's StatusIcon override via `messageIconLabel` prop", () => {
75
75
  const customIconLabel = 'My custom icon label';
76
76
 
77
77
  render(
@@ -86,8 +86,8 @@ describe('Field', () => {
86
86
  </Field>,
87
87
  );
88
88
 
89
- expect(screen.queryByRole('graphics-symbol', { name: 'Error.' })).not.toBeInTheDocument();
90
- expect(screen.getByRole('graphics-symbol', { name: customIconLabel })).toBeInTheDocument();
89
+ expect(screen.queryByLabelText('Error:')).not.toBeInTheDocument();
90
+ expect(screen.getByLabelText(customIconLabel)).toBeInTheDocument();
91
91
  });
92
92
 
93
93
  it('should show or hide (Optional) suffix depending on required prop', () => {
@@ -147,4 +147,20 @@ describe('Field', () => {
147
147
  const label = screen.getByText('Phone number');
148
148
  await userEvent.click(label);
149
149
  });
150
+
151
+ it('should show loading spinner when messageLoading is true', () => {
152
+ render(
153
+ <Field
154
+ label="Phone number"
155
+ message="Processing your request"
156
+ sentiment="neutral"
157
+ messageLoading
158
+ >
159
+ <Input />
160
+ </Field>,
161
+ );
162
+
163
+ expect(screen.getByTestId('InlinePrompt_ProcessIndicator')).toBeInTheDocument();
164
+ expect(screen.getByText('Processing your request')).toBeInTheDocument();
165
+ });
150
166
  });
@@ -46,6 +46,7 @@ export const Basic = (args: FieldProps) => {
46
46
  sentiment={Sentiment.NEGATIVE}
47
47
  message="Validation error, please take a look"
48
48
  messageIconLabel={args.messageIconLabel}
49
+ messageLoading={args.messageLoading}
49
50
  >
50
51
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
51
52
  </Field>
@@ -81,6 +82,7 @@ export const Basic = (args: FieldProps) => {
81
82
  message={lorem10}
82
83
  sentiment="negative"
83
84
  messageIconLabel={args.messageIconLabel}
85
+ messageLoading={args.messageLoading}
84
86
  >
85
87
  <TextArea />
86
88
  </Field>
@@ -109,6 +111,17 @@ export const WithStatusMessages = (args: FieldProps) => {
109
111
  sentiment={Sentiment.POSITIVE}
110
112
  message="Positive message"
111
113
  messageIconLabel={args.messageIconLabel}
114
+ messageLoading={args.messageLoading}
115
+ >
116
+ <Input value={value} onChange={({ target }) => setValue(target.value)} />
117
+ </Field>
118
+
119
+ <Field
120
+ label="Text Input with Proposition Message"
121
+ sentiment="proposition"
122
+ message="Proposition message"
123
+ messageIconLabel={args.messageIconLabel}
124
+ messageLoading={args.messageLoading}
112
125
  >
113
126
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
114
127
  </Field>
@@ -118,6 +131,7 @@ export const WithStatusMessages = (args: FieldProps) => {
118
131
  sentiment={Sentiment.WARNING}
119
132
  message="Warning message"
120
133
  messageIconLabel={args.messageIconLabel}
134
+ messageLoading={args.messageLoading}
121
135
  >
122
136
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
123
137
  </Field>
@@ -127,6 +141,7 @@ export const WithStatusMessages = (args: FieldProps) => {
127
141
  sentiment={Sentiment.NEGATIVE}
128
142
  message="This is a required field"
129
143
  messageIconLabel={args.messageIconLabel}
144
+ messageLoading={args.messageLoading}
130
145
  >
131
146
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
132
147
  </Field>
@@ -138,6 +153,7 @@ export const WithStatusMessages = (args: FieldProps) => {
138
153
  sentiment={Sentiment.NEGATIVE}
139
154
  message="Validation error, please take a look"
140
155
  messageIconLabel={args.messageIconLabel}
156
+ messageLoading={args.messageLoading}
141
157
  >
142
158
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
143
159
  </Field>
@@ -148,6 +164,7 @@ export const WithStatusMessages = (args: FieldProps) => {
148
164
  hint="This is a helpful message"
149
165
  error="Validation error, please take a look"
150
166
  messageIconLabel={args.messageIconLabel}
167
+ messageLoading={args.messageLoading}
151
168
  >
152
169
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
153
170
  </Field>
@@ -157,6 +174,7 @@ export const WithStatusMessages = (args: FieldProps) => {
157
174
  label="Text Input with Info under the field"
158
175
  message="This is a helpful message"
159
176
  messageIconLabel={args.messageIconLabel}
177
+ messageLoading={args.messageLoading}
160
178
  >
161
179
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
162
180
  </Field>
@@ -2,7 +2,7 @@ import { clsx } from 'clsx';
2
2
  import { useId, useRef } from 'react';
3
3
 
4
4
  import { Sentiment } from '../common';
5
- import InlineAlert from '../inlineAlert/InlineAlert';
5
+ import { InlinePrompt, type InlinePromptProps } from '../prompt';
6
6
  import {
7
7
  FieldLabelContextProvider,
8
8
  InputDescribedByProvider,
@@ -21,14 +21,18 @@ export type FieldProps = {
21
21
  hint?: React.ReactNode;
22
22
  message?: React.ReactNode;
23
23
  /**
24
- * Override for the [InlineAlert icon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
24
+ * Override for the [InlinePrompt icon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
25
25
  * announced by the screen readers
26
26
  * */
27
27
  messageIconLabel?: string;
28
+ /**
29
+ * If true, shows a loading spinner in place of the message icon of the InlinePrompt
30
+ */
31
+ messageLoading?: boolean;
28
32
  description?: React.ReactNode;
29
33
  /** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
30
34
  error?: React.ReactNode;
31
- sentiment?: `${Sentiment.NEGATIVE | Sentiment.NEUTRAL | Sentiment.POSITIVE | Sentiment.WARNING}`;
35
+ sentiment?: InlinePromptProps['sentiment'];
32
36
  className?: string;
33
37
  children?: React.ReactNode;
34
38
  };
@@ -39,6 +43,7 @@ export const Field = ({
39
43
  required = true,
40
44
  message: propMessage,
41
45
  messageIconLabel,
46
+ messageLoading,
42
47
  hint,
43
48
  description = hint,
44
49
  sentiment: propType = Sentiment.NEUTRAL,
@@ -104,9 +109,15 @@ export const Field = ({
104
109
  )}
105
110
 
106
111
  {message && (
107
- <InlineAlert type={sentiment} id={messageId} iconLabel={messageIconLabel}>
112
+ <InlinePrompt
113
+ sentiment={sentiment}
114
+ id={messageId}
115
+ iconLabel={messageIconLabel}
116
+ className="np-field__prompt"
117
+ loading={messageLoading}
118
+ >
108
119
  {message}
109
- </InlineAlert>
120
+ </InlinePrompt>
110
121
  )}
111
122
  </div>
112
123
  </InputInvalidProvider>
@@ -4,9 +4,13 @@ import InlineAlert, { InlineAlertProps } from './InlineAlert';
4
4
  import { lorem10, lorem40 } from '../test-utils';
5
5
  import Link from '../link';
6
6
 
7
+ /**
8
+ * > **DEPRECATED** Use [InlinePrompt](?path=/docs/prompts-inlineprompt--docs) or [Field](?path=/docs/forms-field--docs) instead.
9
+ */
7
10
  export default {
8
11
  component: InlineAlert,
9
12
  title: 'Prompts/InlineAlert',
13
+ tags: ['deprecated'],
10
14
  } as Meta<InlineAlertProps>;
11
15
 
12
16
  export const Basic = () => {
@@ -22,13 +22,7 @@ const iconTypes = new Set<NonNullable<InlineAlertProps['type']>>([
22
22
  ]);
23
23
 
24
24
  /**
25
- * Avoid using `<InlineAlert>` component directly
26
- * it's for edge cases when `<Field />` isn't suitable for some reasons.
27
- *
28
- * Example:
29
- * ```
30
- * <Field sentiment={..} message={..}>..</Field>
31
- * ```
25
+ * @deprecated Use [<InlinePrompt />](https://storybook.wise.design/?path=/docs/prompts-inlineprompt--docs) of [<Field />](https://storybook.wise.design/?path=/docs/forms-field--docs) instead.
32
26
  */
33
27
  export default function InlineAlert({
34
28
  id,
package/src/main.css CHANGED
@@ -3822,10 +3822,14 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
3822
3822
  stroke-dasharray: calc(12px * 0.5) calc(12px * 0.5);
3823
3823
  stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
3824
3824
  }
3825
- .np-field-control {
3825
+ .np-field-control,
3826
+ .np-field__prompt {
3826
3827
  margin-top: 4px;
3827
3828
  margin-top: var(--size-4);
3828
3829
  }
3830
+ .np-field:has(.np-checkbox) label {
3831
+ margin-bottom: 0;
3832
+ }
3829
3833
  .np-input-group {
3830
3834
  display: inline-grid;
3831
3835
  width: 100%;
@@ -6920,6 +6924,10 @@ html:not([dir="rtl"]) .np-navigation-option {
6920
6924
  padding: 0;
6921
6925
  margin: 0;
6922
6926
  }
6927
+ .typeahead--prompt {
6928
+ margin-top: 4px;
6929
+ margin-top: var(--size-4);
6930
+ }
6923
6931
  .typeahead-sm.typeahead--multiple .typeahead__input-container {
6924
6932
  min-height: 32px;
6925
6933
  }
@@ -1,4 +1,3 @@
1
- import { useTheme } from '@wise/components-theming';
2
1
  import { clsx } from 'clsx';
3
2
 
4
3
  import Body from '../body';
@@ -23,14 +22,10 @@ const ProgressBar = ({
23
22
  progress,
24
23
  textEnd,
25
24
  }: ProgressBarProps) => {
26
- const { isModern } = useTheme();
27
25
  return (
28
26
  <div className={clsx('np-progress-bar', className)}>
29
27
  <label className="np-progress-bar__title m-b-1" htmlFor={id}>
30
- <Title
31
- type={Typography.TITLE_BODY}
32
- className={isModern ? `m-b-1 text-primary` : `text-primary`}
33
- >
28
+ <Title type={Typography.TITLE_BODY} className="m-b-1 text-primary">
34
29
  {title}
35
30
  </Title>
36
31
  {description && <Body>{description}</Body>}
@@ -163,7 +163,6 @@ export default function Select({
163
163
  const inputAttributes = useInputAttributes();
164
164
 
165
165
  const { formatMessage } = useIntl();
166
- const { isModern } = useTheme();
167
166
  const s = (className: string) => classNamesProp[className] || className;
168
167
  const [open, setOpen] = useState(false);
169
168
  const [searchValue, setSearchValue] = useState(DEFAULT_SEARCH_VALUE);
@@ -599,7 +598,7 @@ export default function Select({
599
598
  </Drawer>
600
599
  ) : (
601
600
  <BottomSheet open={open} onClose={handleCloseOptions}>
602
- {renderOptionsList({ className: isModern ? '' : 'p-a-1' })}
601
+ {renderOptionsList()}
603
602
  </BottomSheet>
604
603
  )
605
604
  ) : (
@@ -103,6 +103,10 @@
103
103
  padding: 0;
104
104
  margin: 0;
105
105
  }
106
+ .typeahead--prompt {
107
+ margin-top: 4px;
108
+ margin-top: var(--size-4);
109
+ }
106
110
  .typeahead-sm.typeahead--multiple .typeahead__input-container {
107
111
  min-height: 32px;
108
112
  }
@@ -122,6 +122,10 @@
122
122
  }
123
123
  }
124
124
 
125
+ &--prompt {
126
+ margin-top: var(--size-4);
127
+ }
128
+
125
129
  // SIZES
126
130
  &-sm {
127
131
  &.typeahead--multiple {
@@ -207,7 +211,7 @@
207
211
  }
208
212
 
209
213
  .np-theme-personal & {
210
- .input-group:not(.disabled,:disabled,.input-group--has-error):focus-within .tw-icon-search {
214
+ .input-group:not(.disabled, :disabled, .input-group--has-error):focus-within .tw-icon-search {
211
215
  color: var(--color-interactive-primary);
212
216
  }
213
217
  .tw-icon-search {
@@ -102,7 +102,7 @@ describe('Typeahead', () => {
102
102
  expect(onChange).toHaveBeenCalledWith([]);
103
103
  });
104
104
 
105
- it('shows InlineAlert when alert prop is provided', () => {
105
+ it('shows InlinePrompt when alert prop is provided', () => {
106
106
  render(
107
107
  <Typeahead
108
108
  id="test"
@@ -1,15 +1,18 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { Search as SearchIcon } from '@transferwise/icons';
3
3
  import { userEvent, within, fn } from 'storybook/test';
4
-
5
- import Typeahead, { type TypeaheadOption } from './Typeahead';
6
- import { Size } from '../common';
7
4
  import { useState } from 'react';
5
+
6
+ import { Sentiment, Size } from '../common';
8
7
  import { Input } from '../inputs/Input';
9
8
  import { Field } from '../field/Field';
10
9
  import Button from '../button';
11
10
  import Modal from '../modal';
11
+ import { InlinePromptProps } from '../prompt';
12
+ import Typeahead, { type TypeaheadOption } from './Typeahead';
12
13
 
14
+ // needed for SB to display correct name in the docs
15
+ (Typeahead as React.FC).displayName = 'Typeahead';
13
16
  type Story = StoryObj<typeof Typeahead>;
14
17
 
15
18
  /**
@@ -37,6 +40,7 @@ const meta: Meta<typeof Typeahead> = {
37
40
  searchDelay: 200,
38
41
  showSuggestions: true,
39
42
  showNewEntry: true,
43
+ alert: undefined,
40
44
  size: Size.MEDIUM,
41
45
  initialValue: [],
42
46
  id: 'myTypeahead',
@@ -54,6 +58,17 @@ const meta: Meta<typeof Typeahead> = {
54
58
  control: 'inline-radio',
55
59
  options: [Size.MEDIUM, Size.LARGE],
56
60
  },
61
+ alert: {
62
+ description:
63
+ 'Displays an [InlinePrompt](?path=/docs/prompts-inlineprompt--docs) below the input when provided. <blockquote class="m-0">**⚠️ `error`, `info` and `success` are deprecated as alert types and will be soon removed.**</blockquote>',
64
+ control: 'object',
65
+ table: {
66
+ type: {
67
+ summary:
68
+ '{ message: ReactNode; type?: "negative" | "neutral" | "positive" | "warning" | "proposition" }',
69
+ },
70
+ },
71
+ },
57
72
  },
58
73
  };
59
74
  export default meta;
@@ -237,6 +252,14 @@ export const Search: Story = {
237
252
  };
238
253
 
239
254
  export const AutoFocusInModal: Story = {
255
+ parameters: {
256
+ docs: {
257
+ story: {
258
+ inline: false,
259
+ iframeHeight: 500,
260
+ },
261
+ },
262
+ },
240
263
  render: function Render() {
241
264
  const [open, setOpen] = useState<boolean>(true);
242
265
  return (
@@ -279,3 +302,57 @@ export const AutoFocusInModal: Story = {
279
302
  );
280
303
  },
281
304
  };
305
+
306
+ export const WithPrompt: Story = {
307
+ args: {
308
+ autoFillOnBlur: undefined,
309
+ chipSeparators: undefined,
310
+ clearable: undefined,
311
+ initialValue: undefined,
312
+ inputAutoComplete: undefined,
313
+ minQueryLength: undefined,
314
+ onBlur: undefined,
315
+ onFocus: undefined,
316
+ onInputChange: undefined,
317
+ onSearch: undefined,
318
+ placeholder: undefined,
319
+ searchDelay: undefined,
320
+ showSuggestions: undefined,
321
+ showNewEntry: undefined,
322
+ size: undefined,
323
+ },
324
+ render: function Render(args) {
325
+ const options = [{ label: 'Option 1' }];
326
+
327
+ const alertTypes: InlinePromptProps['sentiment'][] = [
328
+ Sentiment.NEGATIVE,
329
+ Sentiment.WARNING,
330
+ Sentiment.NEUTRAL,
331
+ Sentiment.POSITIVE,
332
+ 'proposition',
333
+ ];
334
+
335
+ return (
336
+ <>
337
+ {alertTypes.map((sentiment) => (
338
+ <Typeahead
339
+ key={sentiment}
340
+ {...args}
341
+ id={`typeahead-${sentiment}`}
342
+ name={`typeahead-${sentiment}`}
343
+ options={options}
344
+ alert={{
345
+ message: `This is a ${sentiment} message`,
346
+ type: sentiment,
347
+ }}
348
+ />
349
+ ))}
350
+ </>
351
+ );
352
+ },
353
+ decorators: (Story) => (
354
+ <div className="d-flex flex-column">
355
+ <Story />
356
+ </div>
357
+ ),
358
+ };
@@ -16,8 +16,7 @@ import {
16
16
  removeClickClassFromDocumentOnIos,
17
17
  stopPropagation,
18
18
  } from '../common';
19
- import InlineAlert from '../inlineAlert';
20
- import { InlineAlertProps } from '../inlineAlert/InlineAlert';
19
+ import { InlinePrompt, InlinePromptProps } from '../prompt';
21
20
  import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
22
21
 
23
22
  import TypeaheadInput from './typeaheadInput/TypeaheadInput';
@@ -41,8 +40,12 @@ export interface TypeaheadProps<T> extends Partial<WrappedComponentProps> {
41
40
  name: string;
42
41
  addon?: ReactNode;
43
42
  alert?: {
44
- message: InlineAlertProps['children'];
45
- type?: InlineAlertProps['type'];
43
+ message: InlinePromptProps['children'];
44
+ type?:
45
+ | InlinePromptProps['sentiment']
46
+ | `${Sentiment.ERROR}`
47
+ | `${Sentiment.INFO}`
48
+ | `${Sentiment.SUCCESS}`;
46
49
  };
47
50
  allowNew?: boolean;
48
51
  autoFillOnBlur?: boolean;
@@ -474,9 +477,20 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
474
477
  dropdownOpen,
475
478
  });
476
479
 
477
- const alertType = alert?.type ?? Sentiment.NEUTRAL;
478
- const hasError = errorState || (alert && alertType === Sentiment.ERROR);
479
- const displayAlert = (!errorState && alert) || (alert && alertType === Sentiment.ERROR);
480
+ const alertType = (() => {
481
+ if (!alert?.type || alert.type === Sentiment.INFO) {
482
+ return Sentiment.NEUTRAL;
483
+ }
484
+ if (alert.type === Sentiment.ERROR) {
485
+ return Sentiment.NEGATIVE;
486
+ }
487
+ if (alert.type === Sentiment.SUCCESS) {
488
+ return Sentiment.POSITIVE;
489
+ }
490
+ return alert.type;
491
+ })();
492
+ const hasError = errorState || (alert && alertType === Sentiment.NEGATIVE);
493
+ const displayAlert = (!errorState && alert) || (alert && alertType === Sentiment.NEGATIVE);
480
494
  const hasWarning = displayAlert && alertType === Sentiment.WARNING;
481
495
  const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
482
496
  return (
@@ -546,7 +560,13 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
546
560
  </div>
547
561
  )}
548
562
  </div>
549
- {displayAlert ? <InlineAlert type={alert.type}>{alert.message}</InlineAlert> : menu}
563
+ {displayAlert ? (
564
+ <InlinePrompt sentiment={alertType} className="typeahead--prompt">
565
+ {alert.message}
566
+ </InlinePrompt>
567
+ ) : (
568
+ menu
569
+ )}
550
570
  </div>
551
571
  </div>
552
572
  );
@@ -71,7 +71,7 @@ const meta = {
71
71
  },
72
72
  errorIconLabel: {
73
73
  description:
74
- "Override for the InlineAlert icon's default, accessible name announced by the screen readers",
74
+ "Override for the InlinePrompt icon's default, accessible name announced by the screen readers",
75
75
  },
76
76
  usHelpImage: {
77
77
  control: false,