asma-ui-core 3.3.11 → 3.4.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.
|
@@ -1,2 +1,155 @@
|
|
|
1
1
|
import type { StyledDynamicSelectComponent } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A smart, adaptive select input that automatically chooses the best UI representation
|
|
4
|
+
* based on the number of options provided.
|
|
5
|
+
*
|
|
6
|
+
* **Rendering strategy:**
|
|
7
|
+
* - `1–5 options` → renders as an interactive chip group (`DynamicInteractiveChipGroup`).
|
|
8
|
+
* Chips behave as radio buttons (single) or checkboxes (multiple).
|
|
9
|
+
* Long labels automatically switch to a vertical stacked layout.
|
|
10
|
+
* When `!required` and a value is selected, a "Clear selection" button is shown.
|
|
11
|
+
* - `0 or 6+ options` → renders as a searchable autocomplete (`DynamicSelectAutocomplete`).
|
|
12
|
+
* Typing is disabled when there are ≤10 options (caret hidden, keyboard blocked).
|
|
13
|
+
*
|
|
14
|
+
* **Option types:**
|
|
15
|
+
* Options can be primitives (`string | number | boolean`) or objects. When using objects,
|
|
16
|
+
* the component reads `option.value` and `option.label` by default. Override with
|
|
17
|
+
* `valueKey` and `labelKey` to use different keys. Individual options can be disabled
|
|
18
|
+
* by setting `option.disabled = true`.
|
|
19
|
+
*
|
|
20
|
+
* @template TOption – The option type. Must extend `DynamicSelectOption`.
|
|
21
|
+
*
|
|
22
|
+
* ---
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 1. Single select – primitive strings (chip group for ≤5, autocomplete for 6+)
|
|
26
|
+
* const [value, setValue] = useState<string | null>(null)
|
|
27
|
+
*
|
|
28
|
+
* <StyledDynamicSelect
|
|
29
|
+
* dataTest="status-select"
|
|
30
|
+
* options={['Active', 'Inactive', 'Pending']}
|
|
31
|
+
* value={value}
|
|
32
|
+
* onChange={setValue}
|
|
33
|
+
* />
|
|
34
|
+
*
|
|
35
|
+
* ---
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // 2. Single select – object options with default value/label keys
|
|
39
|
+
* type Status = { value: string; label: string; disabled?: boolean }
|
|
40
|
+
* const statuses: Status[] = [
|
|
41
|
+
* { value: 'active', label: 'Active' },
|
|
42
|
+
* { value: 'inactive', label: 'Inactive', disabled: true },
|
|
43
|
+
* ]
|
|
44
|
+
* const [status, setStatus] = useState<Status | null>(null)
|
|
45
|
+
*
|
|
46
|
+
* <StyledDynamicSelect
|
|
47
|
+
* dataTest="status-select"
|
|
48
|
+
* options={statuses}
|
|
49
|
+
* value={status}
|
|
50
|
+
* onChange={setStatus}
|
|
51
|
+
* title="Status"
|
|
52
|
+
* required
|
|
53
|
+
* error={!status}
|
|
54
|
+
* helperText="Status is required"
|
|
55
|
+
* />
|
|
56
|
+
*
|
|
57
|
+
* ---
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // 3. Multiple select – object options with custom keys
|
|
61
|
+
* type User = { id: string; fullName: string; disabled?: boolean }
|
|
62
|
+
* const users: User[] = [
|
|
63
|
+
* { id: '1', fullName: 'Alice Smith' },
|
|
64
|
+
* { id: '2', fullName: 'Bob Jones', disabled: true },
|
|
65
|
+
* ]
|
|
66
|
+
* const [selected, setSelected] = useState<User[] | null>([])
|
|
67
|
+
*
|
|
68
|
+
* <StyledDynamicSelect
|
|
69
|
+
* dataTest="assignee-select"
|
|
70
|
+
* options={users}
|
|
71
|
+
* value={selected}
|
|
72
|
+
* onChange={setSelected}
|
|
73
|
+
* multiple
|
|
74
|
+
* valueKey="id"
|
|
75
|
+
* labelKey="fullName"
|
|
76
|
+
* title="Assignees"
|
|
77
|
+
* placeholder="Search users…"
|
|
78
|
+
* maxTags={3}
|
|
79
|
+
* />
|
|
80
|
+
*
|
|
81
|
+
* ---
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* // 4. Read-only display – only selected options are shown
|
|
85
|
+
* <StyledDynamicSelect
|
|
86
|
+
* dataTest="status-readonly"
|
|
87
|
+
* options={statuses}
|
|
88
|
+
* value={selectedStatus}
|
|
89
|
+
* onChange={() => {}}
|
|
90
|
+
* readOnly
|
|
91
|
+
* />
|
|
92
|
+
*
|
|
93
|
+
* ---
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* // 5. Custom label renderer (e.g. with avatar or description)
|
|
97
|
+
* <StyledDynamicSelect
|
|
98
|
+
* dataTest="user-select"
|
|
99
|
+
* options={users}
|
|
100
|
+
* value={selected}
|
|
101
|
+
* onChange={setSelected}
|
|
102
|
+
* multiple
|
|
103
|
+
* valueKey="id"
|
|
104
|
+
* labelKey="fullName"
|
|
105
|
+
* renderLabel={(user) => (
|
|
106
|
+
* <span className="flex items-center gap-2">
|
|
107
|
+
* <Avatar src={user.avatarUrl} size={20} />
|
|
108
|
+
* {user.fullName}
|
|
109
|
+
* </span>
|
|
110
|
+
* )}
|
|
111
|
+
* />
|
|
112
|
+
*
|
|
113
|
+
* ---
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* // 6. Per-option tooltip
|
|
117
|
+
* <StyledDynamicSelect
|
|
118
|
+
* dataTest="plan-select"
|
|
119
|
+
* options={plans}
|
|
120
|
+
* value={selectedPlan}
|
|
121
|
+
* onChange={setSelectedPlan}
|
|
122
|
+
* getOptionTooltip={(plan) =>
|
|
123
|
+
* plan.disabled ? 'Upgrade your subscription to unlock this plan' : null
|
|
124
|
+
* }
|
|
125
|
+
* />
|
|
126
|
+
*
|
|
127
|
+
* ---
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // 7. Loading state (skeletons in chip group, disabled in autocomplete)
|
|
131
|
+
* <StyledDynamicSelect
|
|
132
|
+
* dataTest="category-select"
|
|
133
|
+
* options={categories}
|
|
134
|
+
* value={value}
|
|
135
|
+
* onChange={setValue}
|
|
136
|
+
* loading={isFetching}
|
|
137
|
+
* />
|
|
138
|
+
*
|
|
139
|
+
* ---
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* // 8. Forwarded ref – focus the input programmatically
|
|
143
|
+
* const ref = useRef<HTMLInputElement>(null)
|
|
144
|
+
*
|
|
145
|
+
* <StyledDynamicSelect
|
|
146
|
+
* ref={ref}
|
|
147
|
+
* dataTest="search-select"
|
|
148
|
+
* options={options}
|
|
149
|
+
* value={value}
|
|
150
|
+
* onChange={setValue}
|
|
151
|
+
* />
|
|
152
|
+
*
|
|
153
|
+
* <button onClick={() => ref.current?.focus()}>Focus</button>
|
|
154
|
+
*/
|
|
2
155
|
export declare const StyledDynamicSelect: StyledDynamicSelectComponent;
|
|
@@ -18,25 +18,75 @@ type MultipleDynamicSelectProps<TOption extends DynamicSelectOption> = {
|
|
|
18
18
|
onChange: (value: TOption[] | null) => void;
|
|
19
19
|
};
|
|
20
20
|
type DynamicSelectCommonProps<TOption extends DynamicSelectOption> = {
|
|
21
|
+
/** Unique identifier used as the root `data-test` attribute for QA selectors. */
|
|
21
22
|
dataTest: string;
|
|
23
|
+
/** The full list of selectable options. Determines which UI is rendered: 1–5 → chip group, 0 or 6+ → autocomplete. */
|
|
22
24
|
options: TOption[];
|
|
25
|
+
/** When `true`, the component is non-interactive and only shows selected value(s) as plain chip(s). */
|
|
23
26
|
readOnly?: boolean;
|
|
27
|
+
/** Prevents the built-in clear button from appearing even when a value is set. Applies to autocomplete only. */
|
|
24
28
|
disableClearable?: boolean;
|
|
29
|
+
/** Optional label rendered above the input/chip group. */
|
|
25
30
|
title?: string;
|
|
31
|
+
/** Controls the visual size of chips and buttons. Defaults to `'medium'`. */
|
|
26
32
|
size?: 'small' | 'medium';
|
|
33
|
+
/** Placeholder text shown in the autocomplete input when no value is selected. */
|
|
27
34
|
placeholder?: string;
|
|
35
|
+
/** Disables all interactions. */
|
|
28
36
|
disabled?: boolean;
|
|
37
|
+
/** Text shown in the autocomplete dropdown when no options match the search query. */
|
|
29
38
|
noOptionsText?: string;
|
|
39
|
+
/** When `true`, shows the error style and renders an error icon next to the helper text. */
|
|
30
40
|
error?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* When `true`, hides the "Clear selection" button in the chip group.
|
|
43
|
+
* Use this when the field must always have a value.
|
|
44
|
+
*/
|
|
45
|
+
required?: boolean;
|
|
46
|
+
/** Supplementary text rendered below the input. Shown as-is, or falls back to `'Required'` when `error=true` and no text is provided. */
|
|
31
47
|
helperText?: React.ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* Key of `TOption` used as the option's identity for comparison and as the chip/tag key.
|
|
50
|
+
* Defaults to `'value'`. Only applicable when `TOption` is an object.
|
|
51
|
+
*/
|
|
32
52
|
valueKey?: TOption extends object ? keyof TOption : never;
|
|
53
|
+
/**
|
|
54
|
+
* Key of `TOption` used as the displayed label string.
|
|
55
|
+
* Defaults to `'label'`. Only applicable when `TOption` is an object.
|
|
56
|
+
*/
|
|
33
57
|
labelKey?: TOption extends object ? keyof TOption : never;
|
|
58
|
+
/**
|
|
59
|
+
* Custom label renderer. When provided, its return value is used instead of the
|
|
60
|
+
* plain string resolved from `labelKey`. Useful for rich content (icons, avatars, etc.).
|
|
61
|
+
*/
|
|
34
62
|
renderLabel?: (option: TOption) => React.ReactNode;
|
|
63
|
+
/**
|
|
64
|
+
* Returns a tooltip node shown when hovering a specific option.
|
|
65
|
+
* Return `null` to show no tooltip for that option.
|
|
66
|
+
*/
|
|
35
67
|
getOptionTooltip?: (option: TOption) => React.ReactNode;
|
|
68
|
+
/** Node prepended inside the autocomplete text input (e.g. a search icon). */
|
|
36
69
|
startAdornment?: React.ReactNode;
|
|
70
|
+
/** Escape hatch to pass any MUI `Autocomplete` prop directly. Applied on top of internal defaults in the autocomplete variant. */
|
|
37
71
|
autocompleteProps?: Partial<AutocompleteProps<TOption, boolean | undefined, boolean | undefined, boolean | undefined>>;
|
|
72
|
+
/**
|
|
73
|
+
* When `true`, renders loading skeletons (chip group) or disables the input (autocomplete)
|
|
74
|
+
* while data is being fetched.
|
|
75
|
+
*/
|
|
38
76
|
loading?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Limits how many selected-value chips are visible in the autocomplete's input area.
|
|
79
|
+
* Remaining selections are summarised as `+N`. Has no effect on the chip group variant.
|
|
80
|
+
*/
|
|
39
81
|
maxTags?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Controls locale-sensitive labels and messages used by the component.
|
|
84
|
+
* Use `'no'` for Norwegian and `'en'` for English.
|
|
85
|
+
*
|
|
86
|
+
* Intended for consumer-facing text such as clear actions, helper copy,
|
|
87
|
+
* or empty-state messaging when localized behavior is supported.
|
|
88
|
+
*/
|
|
89
|
+
locale?: 'no' | 'en';
|
|
40
90
|
};
|
|
41
91
|
export type StyledDynamicSelectProps<TOption extends DynamicSelectOption> = DynamicSelectCommonProps<TOption> & (SingleDynamicSelectProps<TOption> | MultipleDynamicSelectProps<TOption>);
|
|
42
92
|
export type StyledDynamicSelectComponent = <TOption extends DynamicSelectOption>(props: StyledDynamicSelectProps<TOption> & React.RefAttributes<HTMLInputElement>) => React.ReactElement | null;
|