@wallarm-org/design-system 0.27.1-rc-feature-WDS-96-update-data-input-component.1 → 0.27.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.
Files changed (36) hide show
  1. package/dist/components/FilterInput/FilterInput.d.ts +14 -0
  2. package/dist/components/FilterInput/FilterInput.js +5 -1
  3. package/dist/components/FilterInput/index.d.ts +1 -1
  4. package/dist/components/FilterInput/index.js +2 -2
  5. package/dist/components/FilterInput/lib/applyFieldValueTransforms.d.ts +11 -0
  6. package/dist/components/FilterInput/lib/applyFieldValueTransforms.js +43 -0
  7. package/dist/components/FilterInput/lib/applyKnownFieldHelpers.d.ts +21 -0
  8. package/dist/components/FilterInput/lib/applyKnownFieldHelpers.js +29 -0
  9. package/dist/components/FilterInput/lib/index.d.ts +3 -1
  10. package/dist/components/FilterInput/lib/index.js +4 -2
  11. package/dist/components/FilterInput/lib/serializeExpression.d.ts +12 -2
  12. package/dist/components/FilterInput/lib/serializeExpression.js +5 -2
  13. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSerializer.d.ts +16 -0
  14. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSerializer.js +6 -0
  15. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSuggestions.d.ts +5 -4
  16. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSuggestions.js +5 -6
  17. package/dist/components/FilterInput/lib/statusCode/createStatusCodeValidator.d.ts +1 -8
  18. package/dist/components/FilterInput/lib/statusCode/createStatusCodeValidator.js +3 -3
  19. package/dist/components/FilterInput/lib/statusCode/index.d.ts +1 -1
  20. package/dist/components/FilterInput/lib/statusCode/index.js +2 -1
  21. package/dist/components/FilterInput/lib/statusCode/utils/canonicalize.d.ts +2 -2
  22. package/dist/components/FilterInput/lib/statusCode/utils/canonicalize.js +3 -3
  23. package/dist/components/FilterInput/lib/statusCode/utils/constants.d.ts +7 -3
  24. package/dist/components/FilterInput/lib/statusCode/utils/constants.js +3 -3
  25. package/dist/components/FilterInput/lib/statusCode/utils/index.d.ts +1 -3
  26. package/dist/components/FilterInput/lib/statusCode/utils/index.js +2 -3
  27. package/dist/components/FilterInput/types.d.ts +9 -0
  28. package/dist/components/TemporalCore/TemporalSegment.js +2 -2
  29. package/dist/metadata/components.json +2 -2
  30. package/package.json +1 -1
  31. package/dist/components/FilterInput/lib/statusCode/utils/maskRoots.d.ts +0 -4
  32. package/dist/components/FilterInput/lib/statusCode/utils/maskRoots.js +0 -3
  33. package/dist/components/FilterInput/lib/statusCode/utils/types.d.ts +0 -8
  34. package/dist/components/FilterInput/lib/statusCode/utils/types.js +0 -0
  35. package/dist/components/FilterInput/stories/mockStatusCodes.d.ts +0 -6
  36. package/dist/components/FilterInput/stories/mockStatusCodes.js +0 -20
@@ -1,6 +1,20 @@
1
1
  import type { FC, HTMLAttributes } from 'react';
2
2
  import type { ExprNode, FieldMetadata } from './types';
3
3
  export interface FilterInputProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onChange'> {
4
+ /**
5
+ * Filter-field configurations driving the autocomplete. Most fields are
6
+ * passed through as-is, but a few names are **reserved** and auto-wired
7
+ * with design-system helpers (`acceptChar` / `normalize` / `getSuggestions`
8
+ * / `validate`). Current reserved names:
9
+ *
10
+ * - `status_code` — HTTP status code field (mask suggestions, format
11
+ * validation, digit-or-X input filter, partial-input normalization).
12
+ *
13
+ * Consumer-supplied callbacks always override the auto-wiring, so you can
14
+ * opt out per-field. For the same helpers on a field with a different
15
+ * `name`, import the factories (`createStatusCodeSuggestions`, …) and
16
+ * attach them manually.
17
+ */
4
18
  fields?: FieldMetadata[];
5
19
  value?: ExprNode | null;
6
20
  onChange?: (expression: ExprNode | null) => void;
@@ -6,7 +6,8 @@ import { FilterInputErrors, parseFilterInputErrors } from "./FilterInputErrors/i
6
6
  import { FilterInputField } from "./FilterInputField/index.js";
7
7
  import { FilterInputMenu } from "./FilterInputMenu/FilterInputMenu.js";
8
8
  import { useFilterInputAutocomplete, useFilterInputExpression, useFilterInputSelection } from "./hooks/index.js";
9
- const FilterInput = ({ fields = [], value, onChange, placeholder = 'Type to filter...', error = false, showKeyboardHint = false, className, ...props })=>{
9
+ import { applyKnownFieldHelpers } from "./lib/applyKnownFieldHelpers.js";
10
+ const FilterInput = ({ fields: rawFields = [], value, onChange, placeholder = 'Type to filter...', error = false, showKeyboardHint = false, className, ...props })=>{
10
11
  const inputRef = useRef(null);
11
12
  const containerRef = useRef(null);
12
13
  const buildingChipRef = useRef(null);
@@ -15,6 +16,9 @@ const FilterInput = ({ fields = [], value, onChange, placeholder = 'Type to filt
15
16
  if (el) chipRegistryRef.current.set(id, el);
16
17
  else chipRegistryRef.current.delete(id);
17
18
  }, []);
19
+ const fields = useMemo(()=>applyKnownFieldHelpers(rawFields), [
20
+ rawFields
21
+ ]);
18
22
  const { conditions, connectors, chips, upsertCondition, removeCondition, removeConditionAtIndex, clearAll, setConnectorValue } = useFilterInputExpression({
19
23
  fields,
20
24
  value,
@@ -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 { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, type FilterParseError, isFilterParseError, parseExpression, type StatusCodeSuggestionsOptions, serializeExpression, } from './lib';
4
+ export { applyFieldValueTransforms, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, type FilterParseError, isFilterParseError, parseExpression, 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 { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
- export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression };
4
+ import { applyFieldValueTransforms, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
+ export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, applyFieldValueTransforms, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression };
@@ -0,0 +1,11 @@
1
+ import type { ExprNode, FieldMetadata } from '../types';
2
+ /**
3
+ * Walk an expression tree and apply each field's `serializeValue` hook to the
4
+ * matching condition's value. Typical use: consumer has the UI-facing
5
+ * `ExprNode` (e.g. status code chip with `"2XX"`) and wants the backend
6
+ * payload to carry the transformed form (`"2"`).
7
+ *
8
+ * Returns the original node (by reference) when nothing changed, so
9
+ * downstream memoization stays stable.
10
+ */
11
+ export declare const applyFieldValueTransforms: (expr: ExprNode | null, fields: FieldMetadata[]) => ExprNode | null;
@@ -0,0 +1,43 @@
1
+ const transformConditionValue = (condition, fieldsByName)=>{
2
+ const field = fieldsByName.get(condition.field);
3
+ const transform = field?.serializeValue;
4
+ if (!transform) return condition;
5
+ const { value } = condition;
6
+ if (null == value) return condition;
7
+ if (Array.isArray(value)) {
8
+ const next = value.map((v)=>transform(v));
9
+ if (next.every((v, i)=>v === value[i])) return condition;
10
+ return {
11
+ ...condition,
12
+ value: next
13
+ };
14
+ }
15
+ const next = transform(value);
16
+ if (next === value) return condition;
17
+ return {
18
+ ...condition,
19
+ value: next
20
+ };
21
+ };
22
+ const transformGroup = (group, fieldsByName)=>{
23
+ const children = group.children.map((child)=>transformNode(child, fieldsByName));
24
+ if (children.every((c, i)=>c === group.children[i])) return group;
25
+ return {
26
+ ...group,
27
+ children
28
+ };
29
+ };
30
+ const transformNode = (node, fieldsByName)=>{
31
+ if ('condition' === node.type) return transformConditionValue(node, fieldsByName);
32
+ return transformGroup(node, fieldsByName);
33
+ };
34
+ const applyFieldValueTransforms = (expr, fields)=>{
35
+ if (!expr) return expr;
36
+ if (!fields.some((f)=>f.serializeValue)) return expr;
37
+ const fieldsByName = new Map(fields.map((f)=>[
38
+ f.name,
39
+ f
40
+ ]));
41
+ return transformNode(expr, fieldsByName);
42
+ };
43
+ export { applyFieldValueTransforms };
@@ -0,0 +1,21 @@
1
+ import type { FieldMetadata } from '../types';
2
+ /**
3
+ * Decorate `fields` in place with design-system helpers for known field names.
4
+ * `FilterInput` calls this on the `fields` prop before rendering.
5
+ *
6
+ * Reserved names and what they auto-attach (unless the consumer already
7
+ * provided a value for that slot):
8
+ *
9
+ * | `name` | Attaches |
10
+ * | ------------- | ------------------------------------------------------------------------------------------ |
11
+ * | `status_code` | HTTP status code: `acceptChar`, `normalize`, `getSuggestions`, `validate`, `serializeValue` |
12
+ *
13
+ * Consumer-supplied callbacks always win over the auto-wired ones. If the
14
+ * backend uses a different name (e.g. `http_status_code`), the helpers are
15
+ * NOT applied — the consumer must either rename the field to match or wire
16
+ * the factories (`createStatusCode*`) manually.
17
+ *
18
+ * The returned array has **reference-stable identity** when no field matches,
19
+ * so downstream `useMemo` that depends on it does not invalidate unnecessarily.
20
+ */
21
+ export declare const applyKnownFieldHelpers: (fields: FieldMetadata[]) => FieldMetadata[];
@@ -0,0 +1,29 @@
1
+ import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
2
+ const KNOWN_FIELD_HELPERS = {
3
+ status_code: ()=>({
4
+ acceptChar: createStatusCodeInputFilter(),
5
+ normalize: createStatusCodeNormalizer(),
6
+ getSuggestions: createStatusCodeSuggestions(),
7
+ validate: createStatusCodeValidator(),
8
+ serializeValue: createStatusCodeSerializer()
9
+ })
10
+ };
11
+ const applyKnownFieldHelpers = (fields)=>{
12
+ let changed = false;
13
+ const out = fields.map((field)=>{
14
+ const factory = KNOWN_FIELD_HELPERS[field.name];
15
+ if (!factory) return field;
16
+ const helpers = factory();
17
+ changed = true;
18
+ return {
19
+ ...field,
20
+ acceptChar: field.acceptChar ?? helpers.acceptChar,
21
+ normalize: field.normalize ?? helpers.normalize,
22
+ getSuggestions: field.getSuggestions ?? helpers.getSuggestions,
23
+ validate: field.validate ?? helpers.validate,
24
+ serializeValue: field.serializeValue ?? helpers.serializeValue
25
+ };
26
+ });
27
+ return changed ? out : fields;
28
+ };
29
+ export { applyKnownFieldHelpers };
@@ -1,6 +1,8 @@
1
1
  export type { DatePreset } from '../FilterInputMenu/FilterInputDateValueMenu/constants';
2
2
  export { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset, } from '../FilterInputMenu/FilterInputDateValueMenu/constants';
3
3
  export { applyAcceptChar } from './applyAcceptChar';
4
+ export { applyFieldValueTransforms } from './applyFieldValueTransforms';
5
+ export { applyKnownFieldHelpers } from './applyKnownFieldHelpers';
4
6
  export { chipIdToConditionIndex, findChipSplitIndex } from './conditions';
5
7
  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';
6
8
  export { buildContainerAnchoredRect, isMenuRelated } from './dom';
@@ -10,4 +12,4 @@ export { getCurrentValueTokenText, getValueFilterText } from './menuFilterText';
10
12
  export { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator, } from './operators';
11
13
  export { type FilterParseError, isFilterParseError, parseExpression } from './parseExpression';
12
14
  export { serializeExpression } from './serializeExpression';
13
- export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, type StatusCodeSuggestionsOptions, } from './statusCode';
15
+ export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, } from './statusCode';
@@ -1,5 +1,7 @@
1
1
  import { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset } from "../FilterInputMenu/FilterInputDateValueMenu/constants.js";
2
2
  import { applyAcceptChar } from "./applyAcceptChar.js";
3
+ import { applyFieldValueTransforms } from "./applyFieldValueTransforms.js";
4
+ import { applyKnownFieldHelpers } from "./applyKnownFieldHelpers.js";
3
5
  import { chipIdToConditionIndex, findChipSplitIndex } from "./conditions.js";
4
6
  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";
5
7
  import { buildContainerAnchoredRect, isMenuRelated } from "./dom.js";
@@ -9,5 +11,5 @@ import { getCurrentValueTokenText, getValueFilterText } from "./menuFilterText.j
9
11
  import { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator } from "./operators.js";
10
12
  import { isFilterParseError, parseExpression } from "./parseExpression/index.js";
11
13
  import { serializeExpression } from "./serializeExpression.js";
12
- import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
13
- 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, applyAcceptChar, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
14
+ import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
15
+ 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, applyAcceptChar, applyFieldValueTransforms, applyKnownFieldHelpers, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
@@ -1,6 +1,16 @@
1
- import type { ExprNode } from '../types';
1
+ import type { ExprNode, FieldMetadata } from '../types';
2
2
  /**
3
3
  * Serialize an expression tree to a canonical text string.
4
4
  * Top-level conditions are sorted alphabetically by field name.
5
+ *
6
+ * **Pass `fields` when the output targets the backend.** Per-field
7
+ * `serializeValue` hooks run first, so values like the status-code mask
8
+ * `"2XX"` become `"2"` in the emitted string. Omitting `fields` keeps the
9
+ * UI-facing values verbatim — useful for clipboard round-trip, debug
10
+ * display, or any case where the output feeds `parseExpression` back. If
11
+ * you forget `fields` on the path to the backend, the placeholder `X`s
12
+ * ride through silently — name the backend call-site helper explicitly
13
+ * (e.g. `const query = serializeExpression(expr, fields)`) to make the
14
+ * intent obvious at callsite.
5
15
  */
6
- export declare const serializeExpression: (expr: ExprNode | null) => string;
16
+ export declare const serializeExpression: (expr: ExprNode | null, fields?: FieldMetadata[]) => string;
@@ -1,3 +1,4 @@
1
+ import { applyFieldValueTransforms } from "./applyFieldValueTransforms.js";
1
2
  const quoteValue = (v)=>`"${v}"`;
2
3
  const serializeValue = (condition)=>{
3
4
  if ('is_null' === condition.operator || 'is_not_null' === condition.operator) return '';
@@ -27,8 +28,10 @@ const serializeNode = (node, isTopLevel)=>{
27
28
  if ('condition' === node.type) return serializeCondition(node);
28
29
  return serializeGroup(node, isTopLevel);
29
30
  };
30
- const serializeExpression = (expr)=>{
31
+ const serializeExpression = (expr, fields)=>{
31
32
  if (!expr) return '';
32
- return serializeNode(expr, true);
33
+ const normalized = fields ? applyFieldValueTransforms(expr, fields) : expr;
34
+ if (!normalized) return '';
35
+ return serializeNode(normalized, true);
33
36
  };
34
37
  export { serializeExpression };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Build the `serializeValue` callback for the HTTP status code field. Strips
3
+ * trailing mask placeholders (`X` / `x`) from the UI value so the backend
4
+ * receives the shorter class form:
5
+ *
6
+ * "2XX" → "2"
7
+ * "22X" → "22"
8
+ * "222" → "222"
9
+ * "4XX" → "4"
10
+ * "40X" → "40"
11
+ *
12
+ * Anything that isn't a status-code-shaped string (e.g. a number, boolean,
13
+ * or freeform text) is returned unchanged so invalid committed values still
14
+ * surface in the backend payload for the parser to reject.
15
+ */
16
+ export declare const createStatusCodeSerializer: () => ((value: string | number | boolean) => string | number | boolean);
@@ -0,0 +1,6 @@
1
+ const createStatusCodeSerializer = ()=>(value)=>{
2
+ if ('string' != typeof value) return value;
3
+ if (!/^\d[\dX]{0,2}$/i.test(value)) return value;
4
+ return value.replace(/X+$/i, '');
5
+ };
6
+ export { createStatusCodeSerializer };
@@ -1,16 +1,17 @@
1
1
  import type { FieldValueOption } from '../../types';
2
- import { type StatusCodeSuggestionsOptions } from './utils';
3
2
  /**
4
3
  * Build the `getSuggestions` callback for an HTTP status code field. The
5
4
  * returned function:
6
- * - reads the active `maskRoots` from `options.codes`
7
- * - on empty input, offers every available class mask
5
+ * - on empty input, offers every HTTP class mask (`1XX..5XX`)
8
6
  * - on partial input (`"3"`, `"3X"`, `"30"`, …), expands it via
9
7
  * `canonicalizeStatusCodeInput` and offers the single matching mask
10
8
  * - merges any `context.selectedValues` (values already committed to the
11
9
  * chip) in front of the input-driven output so they always appear with
12
10
  * their canonical badge styling
11
+ *
12
+ * No backend config is required — the status-code class range is fixed to
13
+ * `[1..5]` per the HTTP spec.
13
14
  */
14
- export declare const createStatusCodeSuggestions: (options?: StatusCodeSuggestionsOptions) => ((inputText: string, context?: {
15
+ export declare const createStatusCodeSuggestions: () => ((inputText: string, context?: {
15
16
  selectedValues?: Array<string | number | boolean>;
16
17
  }) => FieldValueOption[]);
@@ -1,7 +1,6 @@
1
- import { canonicalizeStatusCodeInput, getMaskRoots, makeMask } from "./utils/index.js";
2
- const createStatusCodeSuggestions = (options)=>{
3
- const maskRoots = getMaskRoots(options?.codes);
4
- const resolveSelected = (selectedValues)=>(selectedValues ?? []).map((v)=>String(v)).filter((s)=>/^\d[\dX]{0,2}$/.test(s) && maskRoots.includes(s.charAt(0))).map((s)=>makeMask(s));
1
+ import { MASK_ROOTS, canonicalizeStatusCodeInput, makeMask } from "./utils/index.js";
2
+ const createStatusCodeSuggestions = ()=>{
3
+ const resolveSelected = (selectedValues)=>(selectedValues ?? []).map((v)=>String(v)).filter((s)=>/^\d[\dX]{0,2}$/.test(s) && MASK_ROOTS.includes(s.charAt(0))).map((s)=>makeMask(s));
5
4
  return (inputText, context)=>{
6
5
  const norm = inputText.trim();
7
6
  const selected = resolveSelected(context?.selectedValues);
@@ -10,8 +9,8 @@ const createStatusCodeSuggestions = (options)=>{
10
9
  ...selected,
11
10
  ...primary.filter((o)=>!seen.has(String(o.value)))
12
11
  ];
13
- if (0 === norm.length) return merge(maskRoots.map((d)=>makeMask(`${d}XX`)));
14
- const mask = canonicalizeStatusCodeInput(norm, maskRoots);
12
+ if (0 === norm.length) return merge(MASK_ROOTS.map((d)=>makeMask(`${d}XX`)));
13
+ const mask = canonicalizeStatusCodeInput(norm);
15
14
  if (!mask) return selected;
16
15
  return merge([
17
16
  makeMask(mask)
@@ -1,4 +1,3 @@
1
- import { type StatusCodeSuggestionsOptions } from './utils';
2
1
  /**
3
2
  * Build a validator for the HTTP status code field. Accepts only 3-character
4
3
  * values whose leading digit is a valid HTTP class (`[1..5]`) and whose
@@ -9,11 +8,5 @@ import { type StatusCodeSuggestionsOptions } from './utils';
9
8
  *
10
9
  * Returns `true` when the value is invalid, matching the
11
10
  * `FieldMetadata.validate` contract.
12
- *
13
- * The `options` argument is accepted for API symmetry with
14
- * `createStatusCodeSuggestions` (same wiring on the field), but validity is
15
- * bound to the static `[1..5]` HTTP class range rather than `codes` — the
16
- * backend's available-data list doesn't narrow what the user may legitimately
17
- * type.
18
11
  */
19
- export declare const createStatusCodeValidator: (_options?: StatusCodeSuggestionsOptions) => ((value: string | number | boolean) => boolean);
12
+ export declare const createStatusCodeValidator: () => ((value: string | number | boolean) => boolean);
@@ -1,8 +1,8 @@
1
- import { STATUS_CODE_LENGTH, VALID_MASK_ROOTS } from "./utils/index.js";
2
- const createStatusCodeValidator = (_options)=>(value)=>{
1
+ import { MASK_ROOTS, STATUS_CODE_LENGTH } from "./utils/index.js";
2
+ const createStatusCodeValidator = ()=>(value)=>{
3
3
  const s = String(value);
4
4
  if (s.length !== STATUS_CODE_LENGTH) return true;
5
- if (!VALID_MASK_ROOTS.has(s.charAt(0))) return true;
5
+ if (!MASK_ROOTS.includes(s.charAt(0))) return true;
6
6
  return !/^(XX|\dX|\d\d)$/.test(s.slice(1));
7
7
  };
8
8
  export { createStatusCodeValidator };
@@ -1,5 +1,5 @@
1
1
  export { createStatusCodeInputFilter } from './createStatusCodeInputFilter';
2
2
  export { createStatusCodeNormalizer } from './createStatusCodeNormalizer';
3
+ export { createStatusCodeSerializer } from './createStatusCodeSerializer';
3
4
  export { createStatusCodeSuggestions } from './createStatusCodeSuggestions';
4
5
  export { createStatusCodeValidator } from './createStatusCodeValidator';
5
- export type { StatusCodeSuggestionsOptions } from './utils';
@@ -1,5 +1,6 @@
1
1
  import { createStatusCodeInputFilter } from "./createStatusCodeInputFilter.js";
2
2
  import { createStatusCodeNormalizer } from "./createStatusCodeNormalizer.js";
3
+ import { createStatusCodeSerializer } from "./createStatusCodeSerializer.js";
3
4
  import { createStatusCodeSuggestions } from "./createStatusCodeSuggestions.js";
4
5
  import { createStatusCodeValidator } from "./createStatusCodeValidator.js";
5
- export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator };
6
+ export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator };
@@ -14,7 +14,7 @@
14
14
  * Rejected:
15
15
  * "3X0" (digit after placeholder)
16
16
  * "X30" (starts with placeholder)
17
- * "6..." (leading digit not in `maskRoots`)
17
+ * "6..." (leading digit outside the `[1..5]` HTTP class range)
18
18
  * "ab..." (non-digit, non-X)
19
19
  */
20
- export declare const canonicalizeStatusCodeInput: (input: string, maskRoots: string[]) => string | null;
20
+ export declare const canonicalizeStatusCodeInput: (input: string) => string | null;
@@ -1,10 +1,10 @@
1
- import { MASK_PLACEHOLDER, STATUS_CODE_LENGTH } from "./constants.js";
2
- const canonicalizeStatusCodeInput = (input, maskRoots)=>{
1
+ import { MASK_PLACEHOLDER, MASK_ROOTS, STATUS_CODE_LENGTH } from "./constants.js";
2
+ const canonicalizeStatusCodeInput = (input)=>{
3
3
  const s = input.toUpperCase();
4
4
  if (0 === s.length || s.length > STATUS_CODE_LENGTH) return null;
5
5
  if (!/^[\dX]+$/.test(s)) return null;
6
6
  const d1 = s.charAt(0);
7
- if (d1 === MASK_PLACEHOLDER || !maskRoots.includes(d1)) return null;
7
+ if (d1 === MASK_PLACEHOLDER || !MASK_ROOTS.includes(d1)) return null;
8
8
  if (1 === s.length) return `${d1}XX`;
9
9
  const c2 = s.charAt(1);
10
10
  if (c2 === MASK_PLACEHOLDER) {
@@ -1,8 +1,12 @@
1
1
  import type { BadgeColor } from '../../../../Badge';
2
2
  /** The five valid HTTP status-code classes, used both for suggestion-building
3
- * and validation. Backed by the standard HTTP spec, independent of what the
4
- * backend happens to carry. */
5
- export declare const VALID_MASK_ROOTS: Set<string>;
3
+ * and validation. Backed by the standard HTTP spec entirely frontend-driven,
4
+ * independent of what the backend config carries.
5
+ *
6
+ * Typed as `readonly string[]` (not `as const`) so `.includes(someString)`
7
+ * stays callable without casting; declaration-file generation rejects the
8
+ * tuple-narrowed overload of `.includes`. */
9
+ export declare const MASK_ROOTS: readonly string[];
6
10
  /** Badge color per HTTP class, keyed by the leading digit. Derived from the
7
11
  * AS-877 Figma designs: informational greys, success greens, redirect blues,
8
12
  * client-error ambers, server-error reds. */
@@ -1,10 +1,10 @@
1
- const VALID_MASK_ROOTS = new Set([
1
+ const MASK_ROOTS = [
2
2
  '1',
3
3
  '2',
4
4
  '3',
5
5
  '4',
6
6
  '5'
7
- ]);
7
+ ];
8
8
  const HTTP_CLASS_BADGE_COLOR = {
9
9
  1: 'slate',
10
10
  2: 'green',
@@ -14,4 +14,4 @@ const HTTP_CLASS_BADGE_COLOR = {
14
14
  };
15
15
  const STATUS_CODE_LENGTH = 3;
16
16
  const MASK_PLACEHOLDER = 'X';
17
- export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS };
17
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, MASK_ROOTS, STATUS_CODE_LENGTH };
@@ -1,5 +1,3 @@
1
1
  export { canonicalizeStatusCodeInput } from './canonicalize';
2
- export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS, } from './constants';
2
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, MASK_ROOTS, STATUS_CODE_LENGTH, } from './constants';
3
3
  export { makeMask } from './makeMask';
4
- export { getMaskRoots } from './maskRoots';
5
- export type { StatusCodeSuggestionsOptions } from './types';
@@ -1,5 +1,4 @@
1
1
  import { canonicalizeStatusCodeInput } from "./canonicalize.js";
2
- import { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS } from "./constants.js";
2
+ import { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, MASK_ROOTS, STATUS_CODE_LENGTH } from "./constants.js";
3
3
  import { makeMask } from "./makeMask.js";
4
- import { getMaskRoots } from "./maskRoots.js";
5
- export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS, canonicalizeStatusCodeInput, getMaskRoots, makeMask };
4
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, MASK_ROOTS, STATUS_CODE_LENGTH, canonicalizeStatusCodeInput, makeMask };
@@ -98,6 +98,15 @@ export interface FieldMetadata {
98
98
  * commits apply this per token.
99
99
  */
100
100
  normalize?: (value: string | number | boolean) => string | number | boolean;
101
+ /**
102
+ * Optional backend-value transformer. The UI stores whatever
103
+ * `normalize` produced (e.g. `"2XX"`), but some backends want a
104
+ * stripped form (e.g. `"2"`). Pass the expression tree through
105
+ * `applyFieldValueTransforms(expr, fields)` — or a string through
106
+ * `serializeExpression(expr, fields)` — to apply this hook when
107
+ * emitting the query. Display in the chip is unaffected.
108
+ */
109
+ serializeValue?: (value: string | number | boolean) => string | number | boolean;
101
110
  }
102
111
  /**
103
112
  * Expression Tree Types
@@ -4,14 +4,14 @@ import { useDateSegment } from "@react-aria/datepicker";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { cn } from "../../utils/cn.js";
6
6
  import { getMonthSegmentText } from "./utils.js";
7
- const segmentVariants = cva(cn('relative outline-none text-left', 'font-sans text-sm transition-all', 'focus:bg-states-primary-pressed', 'focus:outline-none'), {
7
+ const segmentVariants = cva(cn('relative outline-none text-left', 'font-sans text-sm transition-all', 'focus:bg-bg-fill-brand focus:text-text-primary-alt', 'focus:outline-none'), {
8
8
  variants: {
9
9
  isPlaceholder: {
10
10
  true: 'text-text-secondary',
11
11
  false: 'text-text-primary'
12
12
  },
13
13
  disabled: {
14
- true: 'cursor-not-allowed',
14
+ true: 'cursor-not-allowed opacity-50',
15
15
  false: 'hover:bg-states-primary-default-alt cursor-text'
16
16
  },
17
17
  type: {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.27.0",
3
- "generatedAt": "2026-04-20T18:13:10.238Z",
3
+ "generatedAt": "2026-04-21T09:43:50.074Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
@@ -15198,7 +15198,7 @@
15198
15198
  "name": "fields",
15199
15199
  "type": "FieldMetadata[] | undefined",
15200
15200
  "required": false,
15201
- "defaultValue": "[]"
15201
+ "description": "Filter-field configurations driving the autocomplete. Most fields are\npassed through as-is, but a few names are **reserved** and auto-wired\nwith design-system helpers (`acceptChar` / `normalize` / `getSuggestions`\n/ `validate`). Current reserved names:\n\n - `status_code` — HTTP status code field (mask suggestions, format\n validation, digit-or-X input filter, partial-input normalization).\n\nConsumer-supplied callbacks always override the auto-wiring, so you can\nopt out per-field. For the same helpers on a field with a different\n`name`, import the factories (`createStatusCodeSuggestions`, …) and\nattach them manually."
15202
15202
  },
15203
15203
  {
15204
15204
  "name": "value",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.27.1-rc-feature-WDS-96-update-data-input-component.1",
3
+ "version": "0.27.1",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,4 +0,0 @@
1
- /** Extract the HTTP class digits (`1`..`5`) that actually appear as single-
2
- * character entries in the backend `codes` list. These become the "available"
3
- * class masks the helper is willing to emit. */
4
- export declare const getMaskRoots: (codes: string[] | undefined) => string[];
@@ -1,3 +0,0 @@
1
- import { VALID_MASK_ROOTS } from "./constants.js";
2
- const getMaskRoots = (codes)=>(codes ?? []).filter((c)=>1 === c.length && VALID_MASK_ROOTS.has(c));
3
- export { getMaskRoots };
@@ -1,8 +0,0 @@
1
- export interface StatusCodeSuggestionsOptions {
2
- /**
3
- * Raw backend status-code list. The helper keeps only 1-char entries in
4
- * `[1..5]` as mask roots; everything else is ignored. When omitted or
5
- * empty, the helper returns `[]` for every input.
6
- */
7
- codes?: string[];
8
- }
@@ -1,6 +0,0 @@
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[];
@@ -1,20 +0,0 @@
1
- const MOCK_STATUS_CODES = [
2
- '1',
3
- '2',
4
- '3',
5
- '4',
6
- '5',
7
- '101',
8
- '200',
9
- '201',
10
- '301',
11
- '302',
12
- '400',
13
- '401',
14
- '403',
15
- '404',
16
- '500',
17
- '502',
18
- '503'
19
- ];
20
- export { MOCK_STATUS_CODES };