Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Fixed

- We fixed an issue with Dropdown filter captions not updating properly when their template parameters change.

## [3.8.1] - 2026-02-19

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ export interface BaseControllerProps<S extends FilterStore> {
multiselect: boolean;
onChange?: ActionValue;
valueAttribute?: EditableValue<string>;
emptyCaption?: string;
emptySelectionCaption: string;
emptyOptionCaption: string;
ariaLabel: string;
placeholder: string;
}

type Gate<S extends FilterStore> = DerivedPropsGate<BaseControllerProps<S>>;

export class BaseController<S extends FilterStore> implements IJSActionsControlled {
protected readonly gate: Gate<S>;
protected actionHelper: JSActionsHelper;
protected changeHelper: ValueChangeHelper;
protected defaultValue?: Iterable<string>;
Expand All @@ -35,6 +39,7 @@ export class BaseController<S extends FilterStore> implements IJSActionsControll

constructor({ gate, multiselect }: { gate: Gate<S>; multiselect: boolean }) {
const props = gate.props;
this.gate = gate;
this.filterStore = props.filterStore;
this.multiselect = multiselect;
this.serializer = new OptionsSerializer({ store: this.filterStore });
Expand All @@ -47,6 +52,22 @@ export class BaseController<S extends FilterStore> implements IJSActionsControll
this.changeHelper = new ValueChangeHelper(gate, () => this.serializer.value);
}

get emptyCaption(): string {
return this.gate.props.emptySelectionCaption;
}

get ariaLabel(): string {
return this.gate.props.ariaLabel;
}

get emptyOptionCaption(): string {
return this.gate.props.emptyOptionCaption;
}

get inputPlaceholder(): string {
return this.gate.props.placeholder;
}

parseDefaultValue = (value: string | undefined): Iterable<string> | undefined => {
const defaultValue = this.serializer.fromStorableValue(value);
if (!defaultValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EnumFilterStore } from "../stores/EnumFilterStore";
import { BaseController } from "./BaseController";

export class EnumBaseController extends BaseController<EnumFilterStore> {
private readonly gate: DerivedPropsGate<EnumBaseControllerProps>;
protected readonly gate: DerivedPropsGate<EnumBaseControllerProps>;

constructor({ gate, multiselect }: { gate: DerivedPropsGate<EnumBaseControllerProps>; multiselect: boolean }) {
super({ gate, multiselect });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@ import { ComboboxControllerMixin } from "./mixins/ComboboxControllerMixin";
export class EnumComboboxController extends ComboboxControllerMixin(EnumBaseController) {
constructor({ gate }: { gate: DerivedPropsGate<EnumBaseControllerProps> }) {
super({ gate, multiselect: false });
this.inputPlaceholder = gate.props.placeholder;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@ import { SelectControllerMixin } from "./mixins/SelectControllerMixin";
export class EnumSelectController extends SelectControllerMixin(EnumBaseController) {
constructor({ gate }: { gate: DerivedPropsGate<EnumBaseControllerProps> }) {
super({ gate, multiselect: gate.props.multiselect });
this.emptyOption.caption = gate.props.emptyOptionCaption;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ export class EnumTagPickerController extends TagPickerControllerMixin(EnumBaseCo

constructor({ gate }: { gate: DerivedPropsGate<Props> }) {
super({ gate, multiselect: gate.props.multiselect });
this.inputPlaceholder = gate.props.placeholder;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
this.filterSelectedOptions = gate.props.selectionMethod === "rowClick";
this.selectedStyle = gate.props.selectedItemsStyle;
this.selectionMethod = this.selectedStyle === "boxes" ? gate.props.selectionMethod : "checkbox";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { RefBaseController, RefBaseControllerProps } from "./RefBaseController";
export class RefComboboxController extends ComboboxControllerMixin(RefBaseController) {
constructor({ gate }: { gate: DerivedPropsGate<RefBaseControllerProps> }) {
super({ gate, multiselect: false });
this.inputPlaceholder = gate.props.placeholder;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
}

handleFocus = (event: FocusEvent<HTMLInputElement>): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { SelectControllerMixin } from "./mixins/SelectControllerMixin";
export class RefSelectController extends SelectControllerMixin(RefBaseController) {
constructor({ gate }: { gate: DerivedPropsGate<RefBaseControllerProps> }) {
super({ gate, multiselect: gate.props.multiselect });
this.emptyOption.caption = gate.props.emptyOptionCaption;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
}

handleFocus = (): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ export class RefTagPickerController extends TagPickerControllerMixin(RefBaseCont

constructor({ gate }: { gate: DerivedPropsGate<Props> }) {
super({ gate, multiselect: gate.props.multiselect });
this.inputPlaceholder = gate.props.placeholder;
this.emptyCaption = gate.props.emptySelectionCaption;
this.ariaLabel = gate.props.ariaLabel;
this.filterSelectedOptions = gate.props.selectionMethod === "rowClick";
this.selectedStyle = gate.props.selectedItemsStyle;
this.selectionMethod = this.selectedStyle === "boxes" ? gate.props.selectionMethod : "checkbox";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ type BaseController = GConstructor<{
filterStore: FilterStore;
multiselect: boolean;
setup(): () => void;
inputPlaceholder: string;
emptyCaption: string;
ariaLabel: string;
}>;

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function ComboboxControllerMixin<TBase extends BaseController>(Base: TBase) {
return class ComboboxControllerMixin extends Base {
touched = false;
inputValue = "";
inputPlaceholder = "";
emptyCaption = "";
ariaLabel = "";

constructor(...args: any[]) {
super(...args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ export interface FilterStore {
type BaseController = GConstructor<{
filterStore: FilterStore;
multiselect: boolean;
ariaLabel: string;
emptyCaption: string;
emptyOptionCaption: string;
}>;

const none = "[[__none__]]" as const;

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function SelectControllerMixin<TBase extends BaseController>(Base: TBase) {
return class SelectControllerMixin extends Base {
emptyCaption = "";
ariaLabel = "";

readonly emptyOption = {
value: none,
caption: "",
Expand All @@ -45,7 +45,7 @@ export function SelectControllerMixin<TBase extends BaseController>(Base: TBase)
if (this.multiselect) {
return this.filterStore.options;
}
return [this.emptyOption, ...this.filterStore.options];
return [{ ...this.emptyOption, caption: this.emptyOptionCaption }, ...this.filterStore.options];
}

get isEmpty(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ type BaseController = GConstructor<{
filterStore: FilterStore;
multiselect: boolean;
setup(): () => void;
inputPlaceholder: string;
emptyCaption: string;
ariaLabel: string;
}>;

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function TagPickerControllerMixin<TBase extends BaseController>(Base: TBase) {
return class TagPickerControllerMixin extends Base {
touched = false;
inputValue = "";
inputPlaceholder = "";
emptyCaption = "";
ariaLabel = "";
filterSelectedOptions = false;

constructor(...args: any[]) {
Expand Down
Loading