Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
be4f1f9
[Docs][Dashboards API] Add field descriptions to controls schemas
florent-leborgne Apr 15, 2026
5edcd9c
[Docs][Dashboards API] Simplify use_global_filters description
florent-leborgne Apr 16, 2026
6c2904a
[Docs][Dashboards API] Improve controls schema descriptions
florent-leborgne Apr 16, 2026
81c2b9c
[Docs][Dashboards API] Remove redundant min/max/default from time sli…
florent-leborgne Apr 16, 2026
e5ffb59
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 16, 2026
96592e3
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Apr 16, 2026
5c1f0f0
Apply suggestion from @florent-leborgne
florent-leborgne Apr 16, 2026
ad08eb5
Apply suggestion from @nreese
florent-leborgne Apr 16, 2026
6e2122a
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 17, 2026
d91d1b4
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 20, 2026
16d2d8e
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 20, 2026
7341c7e
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 20, 2026
8ba56fa
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 23, 2026
6b1e354
Apply suggestions from code review
florent-leborgne Apr 23, 2026
3a53891
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Apr 23, 2026
214d9b3
Merge branch 'main' into dashboards-api-docs-2d
florent-leborgne Apr 24, 2026
89191e5
argh
florent-leborgne Apr 24, 2026
ae3bad8
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,32 @@ import { DEFAULT_DATA_CONTROL_STATE } from '@kbn/controls-constants';

export const controlTitleSchema = schema.object({
title: schema.maybe(
schema.string({ meta: { description: 'A human-readable title for the control' } })
schema.string({ meta: { description: 'A human-readable title for the control.' } })
),
});

export const dataControlSchema = schema.object({
...controlTitleSchema.getPropSchemas(),
data_view_id: schema.string({
meta: { description: 'The ID of the data view that the control is tied to' }, // this will generate a reference
meta: { description: 'The ID of the data view that provides field options for this control.' }, // this will generate a reference
minLength: 1,
}),
field_name: schema.string({
meta: { description: 'The name of the field in the data view that the control is tied to' },
meta: { description: 'The name of the field in the data view that this control filters on.' },
minLength: 1,
}),
use_global_filters: schema.boolean({
defaultValue: DEFAULT_DATA_CONTROL_STATE.use_global_filters,
meta: {
description:
"When `true`, the control's available options are narrowed by the page's active filters. Defaults to `true`.",
},
}),
ignore_validations: schema.boolean({
defaultValue: DEFAULT_DATA_CONTROL_STATE.ignore_validations,
meta: {
description:
'When `true`, the control skips selection validation and does not report which selections are responsible for returning zero results. Defaults to `false`.',
},
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export const controlWidthSchema = schema.oneOf(
],
{
defaultValue: DEFAULT_PINNED_CONTROL_STATE.width as typeof CONTROL_WIDTH_MEDIUM,
meta: { description: 'Minimum width of the control panel in the control group.' },
meta: {
description: 'Minimum width of the control panel.',
},
}
);

Expand All @@ -39,7 +41,10 @@ export const pinnedControlSchema = schema.object({
width: controlWidthSchema,
grow: schema.boolean({
defaultValue: DEFAULT_PINNED_CONTROL_STATE.grow,
meta: { description: 'Expand width of the control panel to fit available space.' },
meta: {
description:
'When `true`, the control expands to fill any available horizontal space. Defaults to `false`.',
},
}),
});

Expand All @@ -61,6 +66,8 @@ export const getControlsGroupSchema = () => {
meta: {
id: 'kbn-controls-schemas-controls-group-schema-esql-control',
title: ESQL_CONTROL,
description:
'An ES|QL variable control whose selected value is injected into ES|QL visualizations using the `?variable_name` syntax. Options can come from a fixed list or an ES|QL query. Define the options source in `config`.',
},
}
),
Expand All @@ -74,6 +81,8 @@ export const getControlsGroupSchema = () => {
meta: {
id: 'kbn-controls-schemas-controls-group-schema-options-list-control',
title: OPTIONS_LIST_CONTROL,
description:
'A dropdown control that filters data by selecting field values from a data view. Define the data view, field, and selection settings in `config`.',
},
}
),
Expand All @@ -87,6 +96,8 @@ export const getControlsGroupSchema = () => {
meta: {
id: 'kbn-controls-schemas-controls-group-schema-range-slider-control',
title: RANGE_SLIDER_CONTROL,
description:
'A slider control that filters data by selecting a numeric range for the configured field. Define the data view, field, and selection settings in `config`.',
},
}
),
Expand All @@ -100,6 +111,8 @@ export const getControlsGroupSchema = () => {
meta: {
id: 'kbn-controls-schemas-controls-group-schema-time-slider-control',
title: TIME_SLIDER_CONTROL,
description:
'A control panel that filters a time field to a selected sub-range of the global time range. Define the start and end positions in `config` as fractions of the global range (0 to 1).',
},
}
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,83 @@ import { controlTitleSchema, dataControlSchema } from './control_schema';
const SELECTIONS_MAX = 10000;

export const optionsListDisplaySettingsSchema = schema.object({
placeholder: schema.maybe(schema.string()),
hide_action_bar: schema.maybe(schema.boolean()),
hide_exclude: schema.maybe(schema.boolean()),
hide_exists: schema.maybe(schema.boolean()),
hide_sort: schema.maybe(schema.boolean()),
placeholder: schema.maybe(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's neat to have these extra options well defined. This is a good example of a pattern we'll make more use of in the future, where extra customizability is available via the API that doesn't have a direct way to be set via the UI.

schema.string({
meta: {
description: 'Placeholder text displayed in the control input when no option is selected.',
},
})
),
hide_action_bar: schema.maybe(
schema.boolean({
meta: {
description:
'When `true`, the search bar, sorting options, and select all toggle are hidden from the control.',
},
})
),
Comment thread
florent-leborgne marked this conversation as resolved.
hide_exclude: schema.maybe(
schema.boolean({
meta: {
description: 'When `true`, the exclude mode toggle is hidden from the control.',
},
})
),
hide_exists: schema.maybe(
schema.boolean({
meta: {
description: 'When `true`, the exists filter option is hidden from the control.',
},
})
),
hide_sort: schema.maybe(
schema.boolean({
meta: {
description: 'When `true`, the sort selector is hidden from the control.',
},
})
),
});

export const optionsListSearchTechniqueSchema = schema.oneOf(
[schema.literal('prefix'), schema.literal('wildcard'), schema.literal('exact')],
{ defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.search_technique }
{
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.search_technique,
meta: {
description:
'The matching technique used when searching available options. `prefix` matches values starting with the search term, `wildcard` matches values containing the search term, and `exact` requires a complete match. Only applies to string and IP fields. Defaults to `wildcard`.',
},
}
);

export const optionsListSortSchema = schema.object(
{
by: schema.oneOf([schema.literal('_count'), schema.literal('_key')]),
direction: schema.oneOf([schema.literal('asc'), schema.literal('desc')]),
by: schema.oneOf([schema.literal('_count'), schema.literal('_key')], {
meta: {
description:
'The field used to sort the available options list. `_count` sorts by document count and `_key` sorts alphabetically by option value.',
},
}),
direction: schema.oneOf([schema.literal('asc'), schema.literal('desc')], {
meta: {
description: 'The sort direction. `asc` sorts ascending and `desc` sorts descending.',
},
}),
},
{ defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.sort }
{
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.sort,
meta: {
description:
'Defines how the available options are sorted in the control popover. Defaults to `{ by: "_count", direction: "desc" }`.',
},
}
);

export const optionsListSelectionSchema = schema.oneOf([schema.string(), schema.number()]);
export const optionsListSelectionSchema = schema.oneOf([schema.string(), schema.number()], {
meta: {
description: 'A selected option value. Accepts a string or a number.',
},
});

const optionsListControlBaseParameters = schema.object({
display_settings: schema.maybe(optionsListDisplaySettingsSchema),
Expand All @@ -47,35 +103,82 @@ const optionsListControlBaseParameters = schema.object({
export const optionsListDSLControlSchema = schema.object({
...optionsListControlBaseParameters.getPropSchemas(),
...dataControlSchema.getPropSchemas(),
exclude: schema.boolean({ defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.exclude }),
exclude: schema.boolean({
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.exclude,
meta: {
description:
'When `true`, the control filters to documents that do NOT match the selected options. Defaults to `false`.',
},
}),
exists_selected: schema.boolean({
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.exists_selected,
meta: {
description:
"When `true`, the control filters to documents where the field exists, regardless of the field's value. Defaults to `false`.",
},
}),
run_past_timeout: schema.boolean({
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.run_past_timeout,
meta: {
description:
'When `true`, the options list query continues running even if it exceeds the configured timeout threshold. Defaults to `false`.',
},
}),
search_technique: optionsListSearchTechniqueSchema,
selected_options: schema.arrayOf(optionsListSelectionSchema, {
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.selected_options,
maxSize: SELECTIONS_MAX,
meta: {
description: 'The list of currently selected option values.',
},
}),
single_select: schema.boolean({
defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.single_select,
meta: {
description:
'When `true`, only one option can be selected at a time. Selecting a new option deselects any previously selected option. Defaults to `false`.',
},
}),
single_select: schema.boolean({ defaultValue: DEFAULT_DSL_OPTIONS_LIST_STATE.single_select }),
sort: optionsListSortSchema,
});

const baseEsqlControl = {
...controlTitleSchema.getPropSchemas(),
...optionsListControlBaseParameters.getPropSchemas(),
selected_options: schema.arrayOf(schema.string(), { maxSize: SELECTIONS_MAX }),
single_select: schema.boolean({ defaultValue: DEFAULT_ESQL_OPTIONS_LIST_STATE.single_select }),
variable_name: schema.string(),
variable_type: schema.oneOf([
schema.literal('fields'),
schema.literal('values'),
schema.literal('functions'),
schema.literal('time_literal'),
schema.literal('multi_values'),
]),
selected_options: schema.arrayOf(schema.string(), {
maxSize: SELECTIONS_MAX,
meta: {
description: 'The list of currently selected option values.',
},
}),
single_select: schema.boolean({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that we have this duplicated rather than reusing the same schema

defaultValue: DEFAULT_ESQL_OPTIONS_LIST_STATE.single_select,
meta: {
description:
'When `true`, only one option can be selected at a time. Selecting a new option deselects any previously selected option. Defaults to `true`.',
},
}),
variable_name: schema.string({
meta: {
description:
'The name of the ES|QL variable that this control populates. The variable is referenced in ES|QL queries using the `?variable_name` syntax.',
},
}),
variable_type: schema.oneOf(
[
schema.literal('fields'),
schema.literal('values'),
schema.literal('functions'),
schema.literal('time_literal'),
schema.literal('multi_values'),
],
{
meta: {
description:
'The ES|QL variable type that determines how the selected value is substituted into the query. Accepts `fields`, `values`, `functions`, `time_literal`, or `multi_values`.',
},
}
),
};

export const optionsListESQLControlSchema = schema.discriminatedUnion('control_type', [
Expand All @@ -85,23 +188,37 @@ export const optionsListESQLControlSchema = schema.discriminatedUnion('control_t
control_type: schema.literal('STATIC_VALUES'),
available_options: schema.arrayOf(schema.string(), {
maxSize: MAX_OPTIONS_LIST_REQUEST_SIZE,
meta: {
description: 'A fixed list of option strings displayed in the control.',
},
}),
},
{
meta: {
id: 'kbn-controls-schemas-options-list-esql-control-schema-static-values',
title: 'STATIC_VALUES',
description:
'An ES|QL variable control with a fixed list of selectable options defined directly in `available_options`.',
},
}
),
schema.object(
{
...baseEsqlControl,
control_type: schema.literal('VALUES_FROM_QUERY'),
esql_query: schema.string(),
esql_query: schema.string({
meta: {
description:
'An ES|QL query whose results populate the list of available options in the control popover.',
},
}),
},
{
meta: {
id: 'kbn-controls-schemas-options-list-esql-control-schema-values-from-query',
title: 'VALUES_FROM_QUERY',
description:
'An ES|QL variable control whose selectable options are dynamically retrieved by running an ES|QL query.',
},
}
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,23 @@ import { schema } from '@kbn/config-schema';
import { DEFAULT_RANGE_SLIDER_STATE } from '@kbn/controls-constants';
import { dataControlSchema } from './control_schema';

export const rangeValueSchema = schema.arrayOf(schema.string(), { minSize: 2, maxSize: 2 });
export const rangeValueSchema = schema.arrayOf(schema.string(), {
minSize: 2,
maxSize: 2,
meta: {
description:
'The selected range as a two-element array of strings representing the lower and upper bound values, for example `["10", "50"]`.',
},
});

export const rangeSliderControlSchema = schema.object({
...dataControlSchema.getPropSchemas(),
value: schema.maybe(rangeValueSchema),
step: schema.number({ defaultValue: DEFAULT_RANGE_SLIDER_STATE.step, min: 0 }),
step: schema.number({
defaultValue: DEFAULT_RANGE_SLIDER_STATE.step,
min: 0,
meta: {
description: 'The step size between selectable range values.',
},
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,25 @@ export const timeSliderControlSchema = schema.object({
defaultValue: DEFAULT_TIME_SLIDER_STATE.start_percentage_of_time_range,
min: 0,
max: 1,
meta: {
description:
'The start of the selected time window expressed as a fraction of the global time range, where `0` is the beginning and `1` is the end of the range.',
},
}),
end_percentage_of_time_range: schema.number({
defaultValue: DEFAULT_TIME_SLIDER_STATE.end_percentage_of_time_range,
min: 0,
max: 1,
meta: {
description:
'The end of the selected time window expressed as a fraction of the global time range, where `0` is the beginning and `1` is the end of the range.',
},
}),
is_anchored: schema.boolean({
defaultValue: DEFAULT_TIME_SLIDER_STATE.is_anchored,
meta: {
description:
'When `true`, the start of the time window is fixed at the beginning of the global time range. Only the end of the window can be adjusted. Defaults to `false`.',
},
}),
is_anchored: schema.boolean({ defaultValue: DEFAULT_TIME_SLIDER_STATE.is_anchored }),
});
Loading