fix(settings): stop offering a column the backend rejects
Settings -> Track List Columns listed two rows both called "Track Name", one of which could not be ticked, and a screen reader heard "Show the Track Name column" twice with nothing to tell them apart. They are `titleArtist` and `trackName`, and the issue left open which way to fix it: a second label so the sort dropdown and the configurator can say different words, or drop the row because the user cannot select it. The code settles that. `titleArtist` is not in `tracklist.AllColumnIDs`, so `Config.Validate()` returns `unknown track-list column ID: "titleArtist"` -- ticking the row sends a column set the backend rejects, `config-page` swallows the rejection into a `console.error`, and the tick reverts. A second label would have named a control that cannot work. So a definition says whether it is a *choice*, and the configurator reads `CONFIGURABLE_COLUMN_IDS` rather than `Object.keys(COLUMN_DEFS)`. The new test reads Go's own list out of `backend/tracklist/config.go` rather than writing it down a third time, since a third copy is the fault one step earlier. Verified by planting: with the filter removed all four assertions fail with the defect's own numbers. Not fixed here, and filed as #231: `SetTrackListColumns` assigns before it validates, so a rejected list stays in memory and `Save()` validates the whole config -- one tick and no setting saves for the rest of the session. That is reachable from any invalid input, not from this row. Closes #197
This commit is contained in:
@@ -51,7 +51,7 @@ import type { BackgroundShade } from '@store/theme-store';
|
||||
import type { IconStyle } from '@store/favorites-store';
|
||||
import {
|
||||
COLUMN_DEFS,
|
||||
ALL_COLUMN_IDS,
|
||||
CONFIGURABLE_COLUMN_IDS,
|
||||
} from '@components/track-list/columns';
|
||||
|
||||
import './config-field';
|
||||
@@ -1640,7 +1640,7 @@ export class ConfigPage extends ViewLifecycleMixin(LitElement) {
|
||||
...this.trackListCtrl.columnIds,
|
||||
];
|
||||
|
||||
const disabledIds = ALL_COLUMN_IDS.filter(
|
||||
const disabledIds = CONFIGURABLE_COLUMN_IDS.filter(
|
||||
(id) => !enabledIds.includes(id),
|
||||
);
|
||||
|
||||
|
||||
@@ -32,6 +32,18 @@ export interface ColumnDef {
|
||||
id: string;
|
||||
/** Human-readable header label. */
|
||||
label: string;
|
||||
/**
|
||||
* Whether Settings may offer this column. Defaults to true.
|
||||
*
|
||||
* A definition is not the same thing as a *choice*. `titleArtist`
|
||||
* is the phone's stacked column, picked by width in
|
||||
* `PHONE_COLUMN_IDS`, and `tracklist.AllColumnIDs` in Go does not
|
||||
* list it — so a tick in the configurator sends a column set the
|
||||
* backend rejects with `unknown track-list column ID`, the tick
|
||||
* reverts on the next render, and the only trace is a
|
||||
* `console.error` (#197).
|
||||
*/
|
||||
configurable?: boolean;
|
||||
/** Extracts the display value from a track. */
|
||||
accessor: (track: library.Track) => string;
|
||||
/** Default CSS width (used when no saved width exists). */
|
||||
@@ -98,11 +110,16 @@ export const COLUMN_DEFS: Record<string, ColumnDef> = {
|
||||
},
|
||||
titleArtist: {
|
||||
id: 'titleArtist',
|
||||
// Named for what it sorts by, since that is the only place the
|
||||
// label is user-visible: the phone has no column headers, and
|
||||
// the page header's sort list is built from the *configured*
|
||||
// columns rather than the drawn ones.
|
||||
// Named for what it sorts by. That label is drawn nowhere
|
||||
// today: the phone has no column headers, and the page header's
|
||||
// sort list is built from the *configured* columns, which this
|
||||
// one can never be — see `configurable` below.
|
||||
label: 'Track Name',
|
||||
// Chosen by width, never by the user, and rejected by the
|
||||
// backend if it ever were. #197: Settings listed it anyway, so
|
||||
// there were two rows called "Track Name" and the second one
|
||||
// could not be selected.
|
||||
configurable: false,
|
||||
accessor: (t) => t.TrackName,
|
||||
defaultWidth: '1fr',
|
||||
comparator: (a, b) => compareStr(a.TrackName, b.TrackName),
|
||||
@@ -266,10 +283,15 @@ export const COLUMN_DEFS: Record<string, ColumnDef> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* All column IDs in default display order.
|
||||
* Used by the settings UI to list available columns.
|
||||
* The column IDs Settings may offer, in default display order.
|
||||
*
|
||||
* Not every definition is one: a column the user cannot choose has no
|
||||
* row in the configurator, because a checkbox that cannot change
|
||||
* anything is worse than an absent one — see `ColumnDef.configurable`.
|
||||
*/
|
||||
export const ALL_COLUMN_IDS: string[] = Object.keys(COLUMN_DEFS);
|
||||
export const CONFIGURABLE_COLUMN_IDS: string[] = Object.keys(
|
||||
COLUMN_DEFS,
|
||||
).filter((id) => COLUMN_DEFS[id]?.configurable !== false);
|
||||
|
||||
/**
|
||||
* Column IDs that are always searched regardless of visibility.
|
||||
|
||||
Reference in New Issue
Block a user