@ceed/ads 1.33.0 → 1.35.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.
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ export type SearchBarSlot = 'root';
3
+ export interface SearchBarOption {
4
+ label: string;
5
+ value: string;
6
+ placeholder?: string;
7
+ }
8
+ export interface SearchBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
9
+ hideSelect?: boolean;
10
+ options?: SearchBarOption[];
11
+ value: string;
12
+ onChange: (value: string) => void;
13
+ onSearch?: (params: {
14
+ selectValue?: string;
15
+ inputValue: string;
16
+ }) => void;
17
+ }
18
+ export interface SearchBarOwnerState extends Required<Pick<SearchBarProps, 'hideSelect'>> {
19
+ }
20
+ export declare const SearchBar: React.ForwardRefExoticComponent<SearchBarProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { SearchBar } from './SearchBar';
2
+ export type { SearchBarOption, SearchBarOwnerState, SearchBarProps, SearchBarSlot } from './SearchBar';
@@ -8,6 +8,7 @@ import { DateRangePickerProps } from '../DateRangePicker';
8
8
  import { MonthPickerProps } from '../MonthPicker';
9
9
  import { MonthRangePickerProps } from '../MonthRangePicker';
10
10
  import { PercentageInputProps } from '../PercentageInput';
11
+ import type { SearchBarOwnerState, SearchBarProps, SearchBarSlot } from '../SearchBar/SearchBar';
11
12
  declare module '@mui/joy/styles' {
12
13
  interface Components {
13
14
  Calendar?: {
@@ -38,6 +39,10 @@ declare module '@mui/joy/styles' {
38
39
  defaultProps?: Partial<PercentageInputProps>;
39
40
  styleOverrides?: StyleOverrides<'root', {}, Theme>;
40
41
  };
42
+ SearchBar?: {
43
+ defaultProps?: Partial<SearchBarProps>;
44
+ styleOverrides?: StyleOverrides<SearchBarSlot, SearchBarOwnerState, Theme>;
45
+ };
41
46
  }
42
47
  }
43
48
  declare module '@mui/joy/Avatar' {
@@ -205,6 +205,68 @@ Full demonstration of all GFM features.
205
205
  />
206
206
  ````
207
207
 
208
+ ## External Link Target
209
+
210
+ Override the built-in anchor renderer via `markdownOptions.components.a` to detect external links by origin comparison, open them in a new tab, and prepend an `OpenInNew` icon so users can tell external links apart at a glance. Internal links keep `defaultLinkAction` behavior and render without the icon.
211
+
212
+ ```tsx
213
+ <Markdown {...args} markdownOptions={{
214
+ components: {
215
+ a: ({
216
+ href,
217
+ children
218
+ }) => {
219
+ const isExternal = (() => {
220
+ if (!href || typeof window === 'undefined') return false;
221
+ try {
222
+ return new URL(href, window.location.href).origin !== window.location.origin;
223
+ } catch {
224
+ return false;
225
+ }
226
+ })();
227
+ return <Link href={href} target={isExternal ? '_blank' : '_self'} rel={isExternal ? 'noopener noreferrer' : undefined} startDecorator={isExternal ? <OpenInNew sx={{
228
+ fontSize: '1em'
229
+ }} /> : undefined}>
230
+ {children}
231
+ </Link>;
232
+ }
233
+ }
234
+ }} />
235
+ ```
236
+
237
+ ```tsx
238
+ import OpenInNew from '@mui/icons-material/OpenInNew';
239
+ import { Markdown, Link } from '@ceed/ads';
240
+
241
+ <Markdown
242
+ content={content}
243
+ markdownOptions={{
244
+ components: {
245
+ a: ({ href, children }) => {
246
+ const isExternal = (() => {
247
+ if (!href || typeof window === 'undefined') return false;
248
+ try {
249
+ return new URL(href, window.location.href).origin !== window.location.origin;
250
+ } catch {
251
+ return false;
252
+ }
253
+ })();
254
+ return (
255
+ <Link
256
+ href={href}
257
+ target={isExternal ? '_blank' : '_self'}
258
+ rel={isExternal ? 'noopener noreferrer' : undefined}
259
+ startDecorator={isExternal ? <OpenInNew sx={{ fontSize: '1em' }} /> : undefined}
260
+ >
261
+ {children}
262
+ </Link>
263
+ );
264
+ },
265
+ },
266
+ }}
267
+ />
268
+ ```
269
+
208
270
  ## When to Use
209
271
 
210
272
  ### ✅ Good Use Cases
@@ -49,6 +49,8 @@ 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
54
  export { Select, Option } from './Select';
53
55
  export { Sheet } from './Sheet';
54
56
  export { Stack } from './Stack';
@@ -0,0 +1,103 @@
1
+ # SearchBar
2
+
3
+ ## Introduction
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`
13
+
14
+ ```tsx
15
+ <SearchBar options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
16
+ ```
17
+
18
+ | Field | Description | Default |
19
+ | ---------- | ----------- | ------- |
20
+ | hideSelect | — | — |
21
+ | options | — | — |
22
+ | value | — | — |
23
+ | onChange | — | — |
24
+ | onSearch | — | — |
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ import { SearchBar } from '@ceed/ads';
30
+
31
+ <SearchBar
32
+ value={query}
33
+ onChange={setQuery}
34
+ options={[
35
+ { label: 'Account #', value: 'account', placeholder: 'e.g. 1234567' },
36
+ { label: 'Jira Issue #', value: 'jira', placeholder: 'e.g. PROC-1234' },
37
+ ]}
38
+ onSearch={({ selectValue, inputValue }) => console.log(selectValue, inputValue)}
39
+ />
40
+ ```
41
+
42
+ ## Without Select
43
+
44
+ Use `hideSelect` to hide the category Select and render a narrower input-only layout.
45
+
46
+ ```tsx
47
+ <SearchBar hideSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
48
+ ```
49
+
50
+ ## onSearch
51
+
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`.
53
+
54
+ ```tsx
55
+ <Stack gap={2}>
56
+ <SearchBar options={SAMPLE_OPTIONS} value={value} onChange={setValue} onSearch={setLastSearch} />
57
+ <Typography level="body-sm">value: "{value}"</Typography>
58
+ {lastSearch && <Typography level="body-sm">
59
+ onSearch: [{lastSearch.selectValue}] "{lastSearch.inputValue}"
60
+ </Typography>}
61
+ </Stack>
62
+ ```
63
+
64
+ ## Props and Customization
65
+
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`. |
73
+
74
+ > **Note**: Also accepts all standard HTML `div` attributes (`className`, `style`, `sx`, etc.).
75
+
76
+ ### SearchBarOption
77
+
78
+ Each element of the `options` array has the following shape:
79
+
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"`) |
85
+
86
+ ### CSS Custom Properties
87
+
88
+ | Variable | Default | Description |
89
+ | ------------------------ | ------------------------------------- | ----------------------------------------------- |
90
+ | `--ceed-SearchBar-width` | `300px` / `220px` (when `hideSelect`) | Component width; override per instance via `sx` |
91
+
92
+ Override example: `<SearchBar sx={{ '--ceed-SearchBar-width': '400px' }} … />`
93
+
94
+ ## Best Practices
95
+
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.
99
+
100
+ ## Accessibility
101
+
102
+ - 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`.
@@ -21,6 +21,7 @@
21
21
  - [Radio](./RadioButton.md)
22
22
  - [RadioList](./RadioList.md)
23
23
  - [RadioTileGroup](./RadioTileGroup.md)
24
+ - [SearchBar](./SearchBar.md)
24
25
  - [Select](./Select.md)
25
26
  - [Slider](./Slider.md)
26
27
  - [Switch](./Switch.md)