@ceed/ads 1.35.0 → 1.35.1

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.
@@ -1,13 +1,15 @@
1
1
  import * as React from 'react';
2
+ import { type BoxProps } from '@mui/joy/Box';
2
3
  export type SearchBarSlot = 'root';
3
4
  export interface SearchBarOption {
4
5
  label: string;
5
6
  value: string;
6
7
  placeholder?: string;
7
8
  }
8
- export interface SearchBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
9
- hideSelect?: boolean;
9
+ export interface SearchBarProps extends Omit<BoxProps, 'onChange'> {
10
+ showSelect?: boolean;
10
11
  options?: SearchBarOption[];
12
+ placeholder?: string;
11
13
  value: string;
12
14
  onChange: (value: string) => void;
13
15
  onSearch?: (params: {
@@ -15,6 +17,5 @@ export interface SearchBarProps extends Omit<React.HTMLAttributes<HTMLDivElement
15
17
  inputValue: string;
16
18
  }) => void;
17
19
  }
18
- export interface SearchBarOwnerState extends Required<Pick<SearchBarProps, 'hideSelect'>> {
19
- }
20
- export declare const SearchBar: React.ForwardRefExoticComponent<SearchBarProps & React.RefAttributes<HTMLDivElement>>;
20
+ export type SearchBarOwnerState = Required<Pick<SearchBarProps, 'showSelect'>>;
21
+ export declare const SearchBar: React.ForwardRefExoticComponent<Omit<SearchBarProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -1,2 +1,3 @@
1
- export { SearchBar } from './SearchBar';
2
- export type { SearchBarOption, SearchBarOwnerState, SearchBarProps, SearchBarSlot } from './SearchBar';
1
+ import { SearchBar } from './SearchBar';
2
+ export * from './SearchBar';
3
+ export default SearchBar;
@@ -49,8 +49,7 @@ export { ProfileMenu } from './ProfileMenu';
49
49
  export { Radio, RadioGroup } from './Radio';
50
50
  export { RadioTileGroup } from './RadioTileGroup';
51
51
  export { RadioList } from './RadioList';
52
- export { SearchBar } from './SearchBar';
53
- export type { SearchBarOption, SearchBarOwnerState, SearchBarProps, SearchBarSlot } from './SearchBar';
52
+ export { SearchBar, type SearchBarProps, type SearchBarOwnerState, type SearchBarSlot, type SearchBarOption, } from './SearchBar';
54
53
  export { Select, Option } from './Select';
55
54
  export { Sheet } from './Sheet';
56
55
  export { Stack } from './Stack';
@@ -2,58 +2,89 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- A search input component that optionally includes a category Select for filtering search by keyword type.
6
-
7
- > 💡 **Placeholder guidance**
8
- >
9
- > When `options` are provided, each option can include a `placeholder` field to hint at the expected input format for that category. This helps users understand what value to enter.
10
- >
11
- > - Account #: `e.g. 1234567`
12
- > - Jira Issue #: `e.g. PROC-1234`
5
+ A search input component combining a text field and a search button. Optionally includes a category Select that lets users narrow results by keyword type. Hovering over the input while it has a value reveals a clear (✕) button to reset the field. Sizes to its content by default (`inline-flex`) and accepts all `Box` props for layout control.
13
6
 
14
7
  ```tsx
15
- <SearchBar options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
8
+ <SearchBar value={value} onChange={setValue} />
16
9
  ```
17
10
 
18
- | Field | Description | Default |
19
- | ---------- | ----------- | ------- |
20
- | hideSelect | — | — |
21
- | options | — | — |
22
- | value | — | — |
23
- | onChange | — | — |
24
- | onSearch | — | — |
11
+ | Field | Description | Default |
12
+ | ----------- | ----------- | ------- |
13
+ | showSelect | — | — |
14
+ | options | — | — |
15
+ | placeholder | — | — |
16
+ | value | — | — |
17
+ | onChange | — | — |
18
+ | onSearch | — | — |
25
19
 
26
20
  ## Usage
27
21
 
28
22
  ```tsx
29
23
  import { SearchBar } from '@ceed/ads';
30
24
 
25
+ // Basic
26
+ <SearchBar value={query} onChange={setQuery} onSearch={({ inputValue }) => fetch(inputValue)} />
27
+
28
+ // With category Select
31
29
  <SearchBar
32
- value={query}
33
- onChange={setQuery}
30
+ showSelect
34
31
  options={[
35
32
  { label: 'Account #', value: 'account', placeholder: 'e.g. 1234567' },
36
33
  { label: 'Jira Issue #', value: 'jira', placeholder: 'e.g. PROC-1234' },
37
34
  ]}
38
- onSearch={({ selectValue, inputValue }) => console.log(selectValue, inputValue)}
35
+ value={query}
36
+ onChange={setQuery}
37
+ onSearch={({ selectValue, inputValue }) => fetch(selectValue, inputValue)}
39
38
  />
40
39
  ```
41
40
 
42
- ## Without Select
41
+ ## With Select
42
+
43
+ Use `showSelect` together with `options` to display a category Select to the left of the text input. The selected category is passed as `selectValue` in the `onSearch` callback.
44
+
45
+ ```tsx
46
+ <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
47
+ ```
48
+
49
+ ## Clearable Input
50
+
51
+ When the input has a value, hovering over the component reveals a clear (✕) button at the right of the text field. Clicking it calls `onChange('')` without triggering `onSearch`.
52
+
53
+ ```tsx
54
+ <Stack alignItems="flex-start" gap={2}>
55
+ <Typography level="body-sm">Hover over the input to reveal the clear (✕) button.</Typography>
56
+ <SearchBar value={value} onChange={setValue} />
57
+ </Stack>
58
+ ```
59
+
60
+ ## Placeholder
43
61
 
44
- Use `hideSelect` to hide the category Select and render a narrower input-only layout.
62
+ Each `SearchBarOption` can include a `placeholder` field that is shown in the text input while that category is selected. Pass a `placeholder` prop directly to override the option-level placeholder for all categories.
45
63
 
46
64
  ```tsx
47
- <SearchBar hideSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
65
+ <Stack alignItems="flex-start" gap={3}>
66
+ <Stack alignItems="flex-start" gap={1}>
67
+ <Typography level="body-xs" fontWeight="md">
68
+ No placeholder prop — uses the active option's placeholder
69
+ </Typography>
70
+ <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
71
+ </Stack>
72
+ <Stack alignItems="flex-start" gap={1}>
73
+ <Typography level="body-xs" fontWeight="md">
74
+ placeholder="Search by keyword" — overrides option-level placeholder
75
+ </Typography>
76
+ <SearchBar showSelect options={SAMPLE_OPTIONS} placeholder="Search by keyword" value={value} onChange={setValue} />
77
+ </Stack>
78
+ </Stack>
48
79
  ```
49
80
 
50
81
  ## onSearch
51
82
 
52
- `onSearch` fires when the search button is clicked or the Enter key is pressed. It receives both the active `selectValue` (omitted when `hideSelect` is `true`) and the current `inputValue`.
83
+ `onSearch` fires when the search button is clicked or the Enter key is pressed. It receives `inputValue` (always present) and `selectValue` (only present when `showSelect` is `true`).
53
84
 
54
85
  ```tsx
55
- <Stack gap={2}>
56
- <SearchBar options={SAMPLE_OPTIONS} value={value} onChange={setValue} onSearch={setLastSearch} />
86
+ <Stack alignItems="flex-start" gap={2}>
87
+ <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} onSearch={setLastSearch} />
57
88
  <Typography level="body-sm">value: "{value}"</Typography>
58
89
  {lastSearch && <Typography level="body-sm">
59
90
  onSearch: [{lastSearch.selectValue}] "{lastSearch.inputValue}"
@@ -61,43 +92,89 @@ Use `hideSelect` to hide the category Select and render a narrower input-only la
61
92
  </Stack>
62
93
  ```
63
94
 
64
- ## Props and Customization
95
+ When `showSelect` is `false`, `selectValue` is omitted from the payload entirely — not `undefined` as a key, but absent.
96
+
97
+ ```tsx
98
+ <Stack alignItems="flex-start" gap={2}>
99
+ <Typography level="body-sm">
100
+ When <code>showSelect</code> is <code>false</code>, <code>selectValue</code> is omitted from the{' '}
101
+ <code>onSearch</code> payload.
102
+ </Typography>
103
+ <SearchBar value={value} onChange={setValue} onSearch={setLastSearch} />
104
+ {lastSearch && <Stack alignItems="flex-start" gap={0.5}>
105
+ <Typography level="body-sm">inputValue: "{lastSearch.inputValue}"</Typography>
106
+ <Typography level="body-sm" sx={{
107
+ color: lastSearch.selectValue === undefined ? 'success.500' : 'danger.500'
108
+ }}>
109
+ selectValue: {lastSearch.selectValue === undefined ? 'undefined ✅' : `"${lastSearch.selectValue}" ❌`}
110
+ </Typography>
111
+ </Stack>}
112
+ </Stack>
113
+ ```
65
114
 
66
- | Prop | Type | Default | Description |
67
- | ------------ | ---------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
68
- | `value` | `string` | — | Current text input value |
69
- | `onChange` | `(value: string) => void` | — | Called when the text input value changes |
70
- | `onSearch` | `(params: { selectValue?: string; inputValue: string }) => void` | — | Called on search button click or Enter key |
71
- | `hideSelect` | `boolean` | `false` | Hide the category Select and show a narrower input-only layout |
72
- | `options` | `SearchBarOption[]` | — | List of selectable categories rendered in the category Select. Each item may include a `placeholder` hint shown in the text input when that category is active. Required when `hideSelect` is `false`. |
115
+ ## Width
73
116
 
74
- > **Note**: Also accepts all standard HTML `div` attributes (`className`, `style`, `sx`, etc.).
117
+ SearchBar sizes to its content by default. Pass any `Box` width prop to constrain or stretch it.
75
118
 
76
- ### SearchBarOption
119
+ ```tsx
120
+ <Stack alignItems="flex-start" gap={3}>
121
+ <Stack alignItems="flex-start" gap={1}>
122
+ <Typography level="body-xs" fontWeight="md">
123
+ width="100%" — stretches to parent width
124
+ </Typography>
125
+ <Box sx={{
126
+ border: '1px dashed',
127
+ borderColor: 'neutral.300',
128
+ padding: 1
129
+ }}>
130
+ <SearchBar width="100%" value={value} onChange={setValue} />
131
+ </Box>
132
+ </Stack>
133
+ <Stack alignItems="flex-start" gap={1}>
134
+ <Typography level="body-xs" fontWeight="md">
135
+ width={400} — fixed 400px
136
+ </Typography>
137
+ <SearchBar width={400} value={value} onChange={setValue} />
138
+ </Stack>
139
+ <Stack alignItems="flex-start" gap={1}>
140
+ <Typography level="body-xs" fontWeight="md">
141
+ No width prop — sizes to content (inline-flex default)
142
+ </Typography>
143
+ <SearchBar value={value} onChange={setValue} />
144
+ </Stack>
145
+ </Stack>
146
+ ```
77
147
 
78
- Each element of the `options` array has the following shape:
148
+ ## Props and Customization
79
149
 
80
- | Field | Type | Required | Description |
81
- | ------------- | -------- | -------- | ------------------------------------------------------------------------------------------- |
82
- | `label` | `string` | | Display text shown in the Select dropdown |
83
- | `value` | `string` | | Internal identifier passed as `selectValue` in the `onSearch` callback |
84
- | `placeholder` | `string` | — | Hint text shown in the text input while this category is selected (e.g. `"e.g. PROC-1234"`) |
150
+ | Prop | Type | Default | Description |
151
+ | ------------- | ---------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
152
+ | `value` | `string` | | Current value of the text input. Required. |
153
+ | `onChange` | `(value: string) => void` | | Called when the text input value changes. Required. |
154
+ | `onSearch` | `(params: { selectValue?: string; inputValue: string }) => void` | — | Called on search button click or Enter key. |
155
+ | `showSelect` | `boolean` | `false` | Show the category Select alongside the input. |
156
+ | `options` | `SearchBarOption[]` | — | Category options for the Select. Required when `showSelect` is `true`. |
157
+ | `placeholder` | `string` | — | Placeholder text for the input. Takes priority over the option-level `placeholder`. |
85
158
 
86
- ### CSS Custom Properties
159
+ > **Note**: Also accepts all `Box` props (`width`, `sx`, `className`, `style`, etc.).
87
160
 
88
- | Variable | Default | Description |
89
- | ------------------------ | ------------------------------------- | ----------------------------------------------- |
90
- | `--ceed-SearchBar-width` | `300px` / `220px` (when `hideSelect`) | Component width; override per instance via `sx` |
161
+ ### SearchBarOption
91
162
 
92
- Override example: `<SearchBar sx={{ '--ceed-SearchBar-width': '400px' }} … />`
163
+ | Field | Type | Required | Description |
164
+ | ------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
165
+ | `label` | `string` | ✓ | Display text shown in the Select dropdown. |
166
+ | `value` | `string` | ✓ | Identifier passed as `selectValue` in the `onSearch` callback. |
167
+ | `placeholder` | `string` | — | Hint text shown in the input while this category is selected (e.g. `"e.g. PROC-1234"`). Overridden by the top-level `placeholder` prop. |
93
168
 
94
169
  ## Best Practices
95
170
 
96
- - Always provide meaningful `label` values in `options` — they are the primary affordance for selecting a search category.
97
- - Use `placeholder` on each option to show the expected format for that category rather than a generic hint.
98
- - Prefer `onSearch` over reacting to every `onChange` keystroke for server-side search to reduce unnecessary requests.
171
+ - Always provide `options` when `showSelect` is `true` — the Select renders nothing if `options` is omitted.
172
+ - Add a `placeholder` to each option to show the expected input format for that category.
173
+ - Use the top-level `placeholder` prop only when a single hint applies regardless of category.
174
+ - Prefer handling `onSearch` over `onChange` for server-side queries — fire the request once on explicit submission rather than on every keystroke.
99
175
 
100
176
  ## Accessibility
101
177
 
102
178
  - The search button has `aria-label="Search"` and the clear button has `aria-label="Clear"`.
103
- - The text input is a native `<input>` element and supports keyboard navigation; pressing Enter triggers `onSearch`.
179
+ - The text input is a native `<input>` element; pressing Enter triggers `onSearch`.
180
+ - The clear button uses `onMouseDown` with `e.preventDefault()` to prevent the input from losing focus when clicked.