@wallarm-org/design-system 0.25.1 → 0.26.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,5 +1,5 @@
1
1
  import { isValidFieldValue } from "../hooks/useFilterInputAutocomplete/valueCommitHelpers.js";
2
- import { getFieldValues } from "../lib/index.js";
2
+ import { getFieldValues, hasStaticAllowlist } from "../lib/index.js";
3
3
  const parseFilterInputErrors = (conditions, fields)=>{
4
4
  const errors = [];
5
5
  for (const condition of conditions){
@@ -11,7 +11,7 @@ const parseFilterInputErrors = (conditions, fields)=>{
11
11
  errors.push(`Unknown field ${condition.field}`);
12
12
  break;
13
13
  case 'value':
14
- if (field && Array.isArray(condition.value)) {
14
+ if (field && hasStaticAllowlist(field) && Array.isArray(condition.value)) {
15
15
  const fv = getFieldValues(field);
16
16
  if (fv.length > 0) {
17
17
  const invalidValues = condition.value.filter((v)=>!isValidFieldValue(fv, v));
@@ -8,7 +8,8 @@ const FilterInputMenu = ({ fields, autocomplete })=>{
8
8
  const { inputText, menuState, selectedField, selectedOperator, menuPositioning, editingMultiValues, editingSingleValue, editingDateRange, inputRef, menuRef, handleFieldSelect, handleOperatorSelect, handleValueSelect, handleMultiCommit, handleRangeSelect, handleMenuClose, handleMenuDiscard, handleBuildingValueChange, segmentMenuFilterText, editingSegment, blurCommitRef } = autocomplete;
9
9
  const fieldFilterText = 'attribute' === editingSegment ? segmentMenuFilterText : inputText;
10
10
  const operatorFilterText = 'operator' === editingSegment ? segmentMenuFilterText : inputText;
11
- const selectedFieldValues = selectedField ? getFieldValues(selectedField) : [];
11
+ const valueInputText = 'value' === editingSegment ? segmentMenuFilterText : inputText;
12
+ const selectedFieldValues = selectedField ? getFieldValues(selectedField, valueInputText) : [];
12
13
  const valueFilterText = getValueFilterText(editingSegment, inputText, segmentMenuFilterText, selectedOperator, selectedFieldValues);
13
14
  const showOperatorMenu = !!selectedField;
14
15
  const showValueMenu = !!selectedField && !!selectedOperator;
@@ -8,20 +8,15 @@ const ValueMenuItem = ({ option, isChecked, isPending, multiSelect, onSelect })=
8
8
  onSelect: onSelect,
9
9
  className: isPending ? 'bg-states-primary-hover' : void 0,
10
10
  children: [
11
- option.badge ? /*#__PURE__*/ jsxs("div", {
11
+ option.badge ? /*#__PURE__*/ jsx("div", {
12
12
  className: "flex items-center gap-4 px-6 py-2 rounded-4 text-xs font-medium max-w-[320px] min-h-[20px] overflow-clip",
13
13
  style: {
14
14
  backgroundColor: option.badge.color
15
15
  },
16
- children: [
17
- /*#__PURE__*/ jsx("div", {
18
- className: "size-6 rounded-full bg-current"
19
- }),
20
- /*#__PURE__*/ jsx("span", {
21
- className: "min-w-0 truncate leading-4",
22
- children: option.badge.text
23
- })
24
- ]
16
+ children: /*#__PURE__*/ jsx("span", {
17
+ className: "min-w-0 truncate leading-4",
18
+ children: option.badge.text
19
+ })
25
20
  }) : /*#__PURE__*/ jsx("div", {
26
21
  className: "min-w-0",
27
22
  children: /*#__PURE__*/ jsx(Text, {
@@ -1,4 +1,4 @@
1
- import { chipIdToConditionIndex, getDateDisplayLabel, getFieldValues, getOperatorLabel, isMultiSelectOperator } from "../../lib/index.js";
1
+ import { chipIdToConditionIndex, getDateDisplayLabel, getFieldValues, getOperatorLabel, hasStaticAllowlist, isMultiSelectOperator } from "../../lib/index.js";
2
2
  const getEditingCondition = (editingChipId, conditions)=>{
3
3
  if (!editingChipId) return null;
4
4
  const idx = chipIdToConditionIndex(editingChipId);
@@ -12,7 +12,7 @@ const deriveAutocompleteValues = ({ editingChipId, selectedField, selectedOperat
12
12
  const values = Array.isArray(editingCondition.value) ? editingCondition.value : null != editingCondition.value ? [
13
13
  editingCondition.value
14
14
  ] : [];
15
- if (editingCondition.error && selectedField) {
15
+ if (editingCondition.error && selectedField && hasStaticAllowlist(selectedField)) {
16
16
  const fieldValues = getFieldValues(selectedField);
17
17
  if (fieldValues.length > 0) return values.filter((v)=>fieldValues.some((opt)=>opt.value === v));
18
18
  }
@@ -1,8 +1,9 @@
1
1
  import { MIN_DATE_STRING_LENGTH } from "../../FilterInputMenu/FilterInputDateValueMenu/constants.js";
2
- import { chipIdToConditionIndex, getFieldValues, isDatePreset } from "../../lib/index.js";
2
+ import { chipIdToConditionIndex, getFieldValues, hasStaticAllowlist, isDatePreset } from "../../lib/index.js";
3
3
  const findMatchingFieldValue = (fieldValues, text)=>fieldValues.find((v)=>v.label.toLowerCase() === text.toLowerCase() || String(v.value).toLowerCase() === text.toLowerCase());
4
4
  const isValidFieldValue = (fieldValues, v)=>fieldValues.some((opt)=>opt.value === v || String(opt.value).toLowerCase() === String(v).toLowerCase());
5
5
  const getInvalidValueIndices = (field, values)=>{
6
+ if (!hasStaticAllowlist(field)) return [];
6
7
  const fv = getFieldValues(field);
7
8
  if (0 === fv.length) return [];
8
9
  return values.reduce((acc, v, idx)=>{
@@ -26,7 +27,7 @@ const resolveSingleValue = (field, trimmed)=>{
26
27
  const match = findMatchingFieldValue(fv, trimmed);
27
28
  return {
28
29
  resolved: match ? match.value : trimmed,
29
- error: fv.length > 0 && !match ? true : void 0
30
+ error: hasStaticAllowlist(field) && fv.length > 0 && !match ? true : void 0
30
31
  };
31
32
  };
32
33
  const resolveMultiValues = (field, trimmed)=>{
@@ -1,5 +1,5 @@
1
1
  export { FilterInput, type FilterInputProps } from './FilterInput';
2
2
  export { FilterInputChip, type FilterInputChipProps } from './FilterInputField';
3
3
  export { FilterInputFieldMenu, type FilterInputFieldMenuProps, FilterInputOperatorMenu, type FilterInputOperatorMenuProps, FilterInputValueMenu, type FilterInputValueMenuProps, type ValueOption, } from './FilterInputMenu';
4
- export { type FilterParseError, isFilterParseError, parseExpression, serializeExpression, } from './lib';
4
+ export { createStatusCodeSuggestions, type FilterParseError, isFilterParseError, parseExpression, type StatusCodeSuggestionsOptions, serializeExpression, } from './lib';
5
5
  export type { Condition, ExprNode, FieldMetadata, FieldType, FieldValueOption, FilterInputChipData, FilterInputChipVariant, FilterOperator, Group, } from './types';
@@ -1,5 +1,5 @@
1
1
  import { FilterInput } from "./FilterInput.js";
2
2
  import { FilterInputChip } from "./FilterInputField/index.js";
3
3
  import { FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu } from "./FilterInputMenu/index.js";
4
- import { isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
- export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, isFilterParseError, parseExpression, serializeExpression };
4
+ import { createStatusCodeSuggestions, isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
+ export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, createStatusCodeSuggestions, isFilterParseError, parseExpression, serializeExpression };
@@ -1,12 +1,19 @@
1
1
  import type { FieldMetadata, FieldValueOption } from '../types';
2
2
  /**
3
- * Get normalized value options for a field.
4
- * `values` (full FieldValueOption[]) takes precedence over `options` (string[] shorthand).
5
- * `options` strings are converted to `{ value: s, label: s }`.
3
+ * Get value options for a field.
4
+ * Priority: `getSuggestions(inputText)` > `values` > `options` (converted to `{value, label}`).
6
5
  */
7
- export declare const getFieldValues: (field: FieldMetadata) => FieldValueOption[];
6
+ export declare const getFieldValues: (field: FieldMetadata, inputText?: string) => FieldValueOption[];
8
7
  /**
9
- * Check if a field has predefined values (from `values` or `options`).
10
- * Returns false for freeform fields (`options: []` or no values at all).
8
+ * Check whether a field has a source of value suggestions — dynamic callback, static
9
+ * `values`, or `options`. Used to decide whether to render a value dropdown at all.
10
+ * Fields with `getSuggestions` always get a dropdown (empty list is still a list).
11
11
  */
12
12
  export declare const hasFieldValues: (field: FieldMetadata) => boolean;
13
+ /**
14
+ * Whether the field has an exhaustive static allowlist of accepted values.
15
+ * Used by validation code to decide whether to reject values outside the list.
16
+ * Fields with `getSuggestions` return false — their suggestion list is a hint,
17
+ * not an allowlist (consumer may accept freeform values that aren't currently suggested).
18
+ */
19
+ export declare const hasStaticAllowlist: (field: FieldMetadata) => boolean;
@@ -1,10 +1,20 @@
1
- const getFieldValues = (field)=>{
1
+ const getFieldValues = (field, inputText = '')=>{
2
+ if (field.getSuggestions) return field.getSuggestions(inputText);
2
3
  const fromValues = field.values ?? [];
3
- const fromOptions = field.options?.map((s)=>({
4
+ if (fromValues.length > 0) return fromValues;
5
+ return field.options?.map((s)=>({
4
6
  value: s,
5
7
  label: s
6
8
  })) ?? [];
7
- return fromValues.length > 0 ? fromValues : fromOptions;
8
9
  };
9
- const hasFieldValues = (field)=>getFieldValues(field).length > 0;
10
- export { getFieldValues, hasFieldValues };
10
+ const hasFieldValues = (field)=>{
11
+ if (field.getSuggestions) return true;
12
+ if ((field.values ?? []).length > 0) return true;
13
+ return (field.options?.length ?? 0) > 0;
14
+ };
15
+ const hasStaticAllowlist = (field)=>{
16
+ if (field.getSuggestions) return false;
17
+ if ((field.values?.length ?? 0) > 0) return true;
18
+ return (field.options?.length ?? 0) > 0;
19
+ };
20
+ export { getFieldValues, hasFieldValues, hasStaticAllowlist };
@@ -3,9 +3,10 @@ export { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset, } f
3
3
  export { chipIdToConditionIndex, findChipSplitIndex } from './conditions';
4
4
  export { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, OPERATORS_BY_TYPE, QUERY_BAR_SELECTOR, VARIANT_LABELS, } from './constants';
5
5
  export { buildContainerAnchoredRect, isMenuRelated } from './dom';
6
- export { getFieldValues, hasFieldValues } from './fields';
6
+ export { getFieldValues, hasFieldValues, hasStaticAllowlist } from './fields';
7
7
  export { filterAndSort } from './filterSort';
8
8
  export { getValueFilterText } from './menuFilterText';
9
9
  export { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator, } from './operators';
10
10
  export { type FilterParseError, isFilterParseError, parseExpression } from './parseExpression';
11
11
  export { serializeExpression } from './serializeExpression';
12
+ export { createStatusCodeSuggestions, type StatusCodeSuggestionsOptions, } from './statusCodeSuggestions';
@@ -2,10 +2,11 @@ import { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset } fr
2
2
  import { chipIdToConditionIndex, findChipSplitIndex } from "./conditions.js";
3
3
  import { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS } from "./constants.js";
4
4
  import { buildContainerAnchoredRect, isMenuRelated } from "./dom.js";
5
- import { getFieldValues, hasFieldValues } from "./fields.js";
5
+ import { getFieldValues, hasFieldValues, hasStaticAllowlist } from "./fields.js";
6
6
  import { filterAndSort } from "./filterSort.js";
7
7
  import { getValueFilterText } from "./menuFilterText.js";
8
8
  import { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator } from "./operators.js";
9
9
  import { isFilterParseError, parseExpression } from "./parseExpression/index.js";
10
10
  import { serializeExpression } from "./serializeExpression.js";
11
- export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS, buildContainerAnchoredRect, chipIdToConditionIndex, filterAndSort, findChipSplitIndex, formatDateForChip, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
11
+ import { createStatusCodeSuggestions } from "./statusCodeSuggestions.js";
12
+ export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeSuggestions, filterAndSort, findChipSplitIndex, formatDateForChip, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
@@ -0,0 +1,10 @@
1
+ import type { FieldValueOption } from '../types';
2
+ export interface StatusCodeSuggestionsOptions {
3
+ /**
4
+ * Raw backend status-code list. The helper keeps only 1-char entries in
5
+ * `[1..5]` as mask roots; everything else is ignored. When omitted or empty,
6
+ * the helper returns `[]` for every input.
7
+ */
8
+ codes?: string[];
9
+ }
10
+ export declare const createStatusCodeSuggestions: (options?: StatusCodeSuggestionsOptions) => ((inputText: string) => FieldValueOption[]);
@@ -0,0 +1,45 @@
1
+ const VALID_MASK_ROOTS = new Set([
2
+ '1',
3
+ '2',
4
+ '3',
5
+ '4',
6
+ '5'
7
+ ]);
8
+ const HTTP_CLASS_BADGE_COLOR = {
9
+ 1: 'var(--color-bg-light-success)',
10
+ 2: 'var(--color-bg-success)',
11
+ 3: 'var(--color-bg-info)',
12
+ 4: 'var(--color-bg-warning)',
13
+ 5: 'var(--color-bg-danger)'
14
+ };
15
+ const createStatusCodeSuggestions = (options)=>{
16
+ const maskRoots = (options?.codes ?? []).filter((c)=>1 === c.length && VALID_MASK_ROOTS.has(c));
17
+ const makeMask = (label)=>{
18
+ const color = HTTP_CLASS_BADGE_COLOR[label.charAt(0)];
19
+ if (!color) throw new Error(`statusCodeSuggestions: no badge color for mask "${label}"`);
20
+ return {
21
+ value: label,
22
+ label,
23
+ badge: {
24
+ color,
25
+ text: label
26
+ }
27
+ };
28
+ };
29
+ return (inputText)=>{
30
+ const norm = inputText.trim();
31
+ if (norm.length > 3) return [];
32
+ if (norm.length > 0 && !/^\d+$/.test(norm)) return [];
33
+ if (0 === norm.length) return maskRoots.map((d)=>makeMask(`${d}XX`));
34
+ const d1 = norm.charAt(0);
35
+ if (!maskRoots.includes(d1)) return [];
36
+ if (1 === norm.length) return [
37
+ makeMask(`${norm}XX`)
38
+ ];
39
+ if (2 === norm.length) return [
40
+ makeMask(`${norm}X`)
41
+ ];
42
+ return [];
43
+ };
44
+ };
45
+ export { createStatusCodeSuggestions };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Example HTTP status code backend response used by FilterInput stories.
3
+ * Mixes 1-char HTTP classes with 3-char concrete codes — the same shape the
4
+ * real backend produces. See AS-877 design spec.
5
+ */
6
+ export declare const MOCK_STATUS_CODES: string[];
@@ -0,0 +1,18 @@
1
+ const MOCK_STATUS_CODES = [
2
+ '2',
3
+ '3',
4
+ '4',
5
+ '5',
6
+ '200',
7
+ '201',
8
+ '301',
9
+ '302',
10
+ '400',
11
+ '401',
12
+ '403',
13
+ '404',
14
+ '500',
15
+ '502',
16
+ '503'
17
+ ];
18
+ export { MOCK_STATUS_CODES };
@@ -61,6 +61,13 @@ export interface FieldMetadata {
61
61
  * Empty array `[]` means freeform input — no dropdown, user types any value.
62
62
  */
63
63
  options?: string[];
64
+ /**
65
+ * Optional callback to compute value suggestions dynamically from the current
66
+ * input text. When provided, takes precedence over `values` and `options`.
67
+ * The returned list is still post-filtered by `filterAndSort`, so prefix/includes
68
+ * matching still applies — return items that contain the input text.
69
+ */
70
+ getSuggestions?: (inputText: string) => FieldValueOption[];
64
71
  }
65
72
  /**
66
73
  * Expression Tree Types
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.25.0",
3
- "generatedAt": "2026-04-17T10:31:02.556Z",
2
+ "version": "0.25.1",
3
+ "generatedAt": "2026-04-20T10:37:05.301Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.25.1",
3
+ "version": "0.26.0",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",