@wordpress/views 1.7.1-next.v.202602241322.0 → 1.8.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.
@@ -24,11 +24,26 @@ __export(filter_utils_exports, {
24
24
  stripActiveViewOverrides: () => stripActiveViewOverrides
25
25
  });
26
26
  module.exports = __toCommonJS(filter_utils_exports);
27
+ var SCALAR_VALUES = [
28
+ "titleField",
29
+ "mediaField",
30
+ "descriptionField",
31
+ "showTitle",
32
+ "showMedia",
33
+ "showDescription",
34
+ "showLevels",
35
+ "infiniteScrollEnabled"
36
+ ];
27
37
  function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
28
38
  if (!activeViewOverrides) {
29
39
  return view;
30
40
  }
31
41
  let result = view;
42
+ for (const key of SCALAR_VALUES) {
43
+ if (key in activeViewOverrides) {
44
+ result = { ...result, [key]: activeViewOverrides[key] };
45
+ }
46
+ }
32
47
  if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
33
48
  const activeFields = new Set(
34
49
  activeViewOverrides.filters.map((f) => f.field)
@@ -50,6 +65,21 @@ function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
50
65
  };
51
66
  }
52
67
  }
68
+ if (activeViewOverrides.layout) {
69
+ result = {
70
+ ...result,
71
+ layout: {
72
+ ...result.layout,
73
+ ...activeViewOverrides.layout
74
+ }
75
+ };
76
+ }
77
+ if (activeViewOverrides.groupBy) {
78
+ result = {
79
+ ...result,
80
+ groupBy: activeViewOverrides.groupBy
81
+ };
82
+ }
53
83
  return result;
54
84
  }
55
85
  function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
@@ -57,6 +87,12 @@ function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
57
87
  return view;
58
88
  }
59
89
  let result = view;
90
+ for (const key of SCALAR_VALUES) {
91
+ if (key in activeViewOverrides) {
92
+ const { [key]: _, ...rest } = result;
93
+ result = rest;
94
+ }
95
+ }
60
96
  if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
61
97
  const activeFields = new Set(
62
98
  activeViewOverrides.filters.map((f) => f.field)
@@ -74,6 +110,20 @@ function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
74
110
  sort: defaultView?.sort
75
111
  };
76
112
  }
113
+ if (activeViewOverrides.layout && "layout" in result && result.layout) {
114
+ const layout = { ...result.layout };
115
+ for (const key of Object.keys(activeViewOverrides.layout)) {
116
+ delete layout[key];
117
+ }
118
+ result = {
119
+ ...result,
120
+ layout: Object.keys(layout).length > 0 ? layout : void 0
121
+ };
122
+ }
123
+ if (activeViewOverrides.groupBy && "groupBy" in result) {
124
+ const { groupBy: _, ...rest } = result;
125
+ result = rest;
126
+ }
77
127
  return result;
78
128
  }
79
129
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/filter-utils.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\ntype ActiveViewOverrides = {\n\tfilters?: Filter[];\n\tsort?: View[ 'sort' ];\n};\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\treturn result;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,UAAM,aAAc,KAAK,WAAW,CAAC,GAAI;AAAA,MACxC,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,IAC9C;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,SAAS,CAAE,GAAG,WAAW,GAAG,oBAAoB,OAAQ;AAAA,IACzD;AAAA,EACD;AAGA,MAAK,oBAAoB,MAAO;AAC/B,UAAM,gBACL,eACA,KAAK,MAAM,UAAU,YAAY,MAAM,SACvC,KAAK,MAAM,cAAc,YAAY,MAAM;AAE5C,QAAK,eAAgB;AACpB,eAAS;AAAA,QACR,GAAG;AAAA,QACH,MAAM,oBAAoB;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAYO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,UAAW,KAAK,WAAW,CAAC,GAAI;AAAA,QAC/B,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,MAC9C;AAAA,IACD;AAAA,EACD;AAGA,MACC,oBAAoB,QACpB,KAAK,MAAM,UAAU,oBAAoB,KAAK,SAC9C,KAAK,MAAM,cAAc,oBAAoB,KAAK,WACjD;AACD,aAAS;AAAA,MACR,GAAG;AAAA,MACH,MAAM,aAAa;AAAA,IACpB;AAAA,EACD;AAEA,SAAO;AACR;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport type { ActiveViewOverrides } from './types';\n\nconst SCALAR_VALUES = [\n\t'titleField',\n\t'mediaField',\n\t'descriptionField',\n\t'showTitle',\n\t'showMedia',\n\t'showDescription',\n\t'showLevels',\n\t'infiniteScrollEnabled',\n] as const;\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge scalar overrides \u2014 always win over persisted values\n\tfor ( const key of SCALAR_VALUES ) {\n\t\tif ( key in activeViewOverrides ) {\n\t\t\tresult = { ...result, [ key ]: activeViewOverrides[ key ] };\n\t\t}\n\t}\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\t// Merge layout \u2014 shallow merge, override keys always win\n\tif ( activeViewOverrides.layout ) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tlayout: {\n\t\t\t\t...( result as any ).layout,\n\t\t\t\t...activeViewOverrides.layout,\n\t\t\t},\n\t\t} as View;\n\t}\n\n\t// Merge groupBy \u2014 full replacement, override always wins\n\tif ( activeViewOverrides.groupBy ) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tgroupBy: activeViewOverrides.groupBy,\n\t\t};\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip scalar keys managed by overrides\n\tfor ( const key of SCALAR_VALUES ) {\n\t\tif ( key in activeViewOverrides ) {\n\t\t\tconst { [ key ]: _, ...rest } = result;\n\t\t\tresult = rest as View;\n\t\t}\n\t}\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\t// Strip layout keys managed by overrides\n\tif ( activeViewOverrides.layout && 'layout' in result && result.layout ) {\n\t\tconst layout = { ...result.layout } as Record< string, unknown >;\n\t\tfor ( const key of Object.keys( activeViewOverrides.layout ) ) {\n\t\t\tdelete layout[ key ];\n\t\t}\n\t\tresult = {\n\t\t\t...result,\n\t\t\tlayout: Object.keys( layout ).length > 0 ? layout : undefined,\n\t\t} as View;\n\t}\n\n\t// Strip groupBy managed by overrides\n\tif ( activeViewOverrides.groupBy && 'groupBy' in result ) {\n\t\tconst { groupBy: _, ...rest } = result;\n\t\tresult = rest as View;\n\t}\n\n\treturn result;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/types.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\nexport interface ViewConfig {\n\t/**\n\t * Entity kind (e.g. postType, root).\n\t * Similar to core-data entity kinds.\n\t */\n\tkind: string;\n\n\t/**\n\t * Entity name (e.g. post, page, user, site).\n\t * Similar to core-data entity names.\n\t */\n\tname: string;\n\n\t/**\n\t * Identifier for the current view (all, published, mine, etc.)\n\t */\n\tslug: string;\n\n\t/**\n\t * Default view configuration\n\t */\n\tdefaultView: View;\n\n\t/**\n\t * View overrides applied on top of the persisted view but never persisted.\n\t * These represent tab-specific configuration (filters, sort) that should\n\t * override the persisted view settings.\n\t */\n\tactiveViewOverrides?: {\n\t\tfilters?: Filter[];\n\t\tsort?: View[ 'sort' ];\n\t};\n\n\t/**\n\t * Optional query parameters from URL (page, search)\n\t */\n\tqueryParams?: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t};\n\n\t/**\n\t * Callback for when URL query parameters should change\n\t */\n\tonChangeQueryParams?: ( params: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t} ) => void;\n}\n"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View } from '@wordpress/dataviews';\n\nexport type ActiveViewOverrides = {\n\t// scalar values\n\ttitleField?: View[ 'titleField' ];\n\tshowTitle?: View[ 'showTitle' ];\n\tmediaField?: View[ 'mediaField' ];\n\tshowMedia?: View[ 'showMedia' ];\n\tdescriptionField?: View[ 'descriptionField' ];\n\tshowDescription?: View[ 'showDescription' ];\n\tshowLevels?: View[ 'showLevels' ];\n\tinfiniteScrollEnabled?: View[ 'infiniteScrollEnabled' ];\n\t// array & object values\n\tfilters?: View[ 'filters' ];\n\tsort?: View[ 'sort' ];\n\tgroupBy?: View[ 'groupBy' ];\n\tlayout?: Record< string, unknown >;\n};\n\nexport interface ViewConfig {\n\t/**\n\t * Entity kind (e.g. postType, root).\n\t * Similar to core-data entity kinds.\n\t */\n\tkind: string;\n\n\t/**\n\t * Entity name (e.g. post, page, user, site).\n\t * Similar to core-data entity names.\n\t */\n\tname: string;\n\n\t/**\n\t * Identifier for the current view (all, published, mine, etc.)\n\t */\n\tslug: string;\n\n\t/**\n\t * Default view configuration\n\t */\n\tdefaultView: View;\n\n\t/**\n\t * View overrides applied on top of the persisted view but never persisted.\n\t * These represent tab-specific configuration (filters, sort) and\n\t * developer-defined view defaults that should override the persisted\n\t * view settings.\n\t */\n\tactiveViewOverrides?: ActiveViewOverrides;\n\n\t/**\n\t * Optional query parameters from URL (page, search)\n\t */\n\tqueryParams?: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t};\n\n\t/**\n\t * Callback for when URL query parameters should change\n\t */\n\tonChangeQueryParams?: ( params: {\n\t\tpage?: number;\n\t\tsearch?: string;\n\t} ) => void;\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,24 @@
1
1
  // packages/views/src/filter-utils.ts
2
+ var SCALAR_VALUES = [
3
+ "titleField",
4
+ "mediaField",
5
+ "descriptionField",
6
+ "showTitle",
7
+ "showMedia",
8
+ "showDescription",
9
+ "showLevels",
10
+ "infiniteScrollEnabled"
11
+ ];
2
12
  function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
3
13
  if (!activeViewOverrides) {
4
14
  return view;
5
15
  }
6
16
  let result = view;
17
+ for (const key of SCALAR_VALUES) {
18
+ if (key in activeViewOverrides) {
19
+ result = { ...result, [key]: activeViewOverrides[key] };
20
+ }
21
+ }
7
22
  if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
8
23
  const activeFields = new Set(
9
24
  activeViewOverrides.filters.map((f) => f.field)
@@ -25,6 +40,21 @@ function mergeActiveViewOverrides(view, activeViewOverrides, defaultView) {
25
40
  };
26
41
  }
27
42
  }
43
+ if (activeViewOverrides.layout) {
44
+ result = {
45
+ ...result,
46
+ layout: {
47
+ ...result.layout,
48
+ ...activeViewOverrides.layout
49
+ }
50
+ };
51
+ }
52
+ if (activeViewOverrides.groupBy) {
53
+ result = {
54
+ ...result,
55
+ groupBy: activeViewOverrides.groupBy
56
+ };
57
+ }
28
58
  return result;
29
59
  }
30
60
  function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
@@ -32,6 +62,12 @@ function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
32
62
  return view;
33
63
  }
34
64
  let result = view;
65
+ for (const key of SCALAR_VALUES) {
66
+ if (key in activeViewOverrides) {
67
+ const { [key]: _, ...rest } = result;
68
+ result = rest;
69
+ }
70
+ }
35
71
  if (activeViewOverrides.filters && activeViewOverrides.filters.length > 0) {
36
72
  const activeFields = new Set(
37
73
  activeViewOverrides.filters.map((f) => f.field)
@@ -49,6 +85,20 @@ function stripActiveViewOverrides(view, activeViewOverrides, defaultView) {
49
85
  sort: defaultView?.sort
50
86
  };
51
87
  }
88
+ if (activeViewOverrides.layout && "layout" in result && result.layout) {
89
+ const layout = { ...result.layout };
90
+ for (const key of Object.keys(activeViewOverrides.layout)) {
91
+ delete layout[key];
92
+ }
93
+ result = {
94
+ ...result,
95
+ layout: Object.keys(layout).length > 0 ? layout : void 0
96
+ };
97
+ }
98
+ if (activeViewOverrides.groupBy && "groupBy" in result) {
99
+ const { groupBy: _, ...rest } = result;
100
+ result = rest;
101
+ }
52
102
  return result;
53
103
  }
54
104
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/filter-utils.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\ntype ActiveViewOverrides = {\n\tfilters?: Filter[];\n\tsort?: View[ 'sort' ];\n};\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\treturn result;\n}\n"],
5
- "mappings": ";AAoBO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,UAAM,aAAc,KAAK,WAAW,CAAC,GAAI;AAAA,MACxC,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,IAC9C;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,SAAS,CAAE,GAAG,WAAW,GAAG,oBAAoB,OAAQ;AAAA,IACzD;AAAA,EACD;AAGA,MAAK,oBAAoB,MAAO;AAC/B,UAAM,gBACL,eACA,KAAK,MAAM,UAAU,YAAY,MAAM,SACvC,KAAK,MAAM,cAAc,YAAY,MAAM;AAE5C,QAAK,eAAgB;AACpB,eAAS;AAAA,QACR,GAAG;AAAA,QACH,MAAM,oBAAoB;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAYO,SAAS,yBACf,MACA,qBACA,aACO;AACP,MAAK,CAAE,qBAAsB;AAC5B,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAGb,MACC,oBAAoB,WACpB,oBAAoB,QAAQ,SAAS,GACpC;AACD,UAAM,eAAe,IAAI;AAAA,MACxB,oBAAoB,QAAQ,IAAK,CAAE,MAAO,EAAE,KAAM;AAAA,IACnD;AACA,aAAS;AAAA,MACR,GAAG;AAAA,MACH,UAAW,KAAK,WAAW,CAAC,GAAI;AAAA,QAC/B,CAAE,MAAe,CAAE,aAAa,IAAK,EAAE,KAAM;AAAA,MAC9C;AAAA,IACD;AAAA,EACD;AAGA,MACC,oBAAoB,QACpB,KAAK,MAAM,UAAU,oBAAoB,KAAK,SAC9C,KAAK,MAAM,cAAc,oBAAoB,KAAK,WACjD;AACD,aAAS;AAAA,MACR,GAAG;AAAA,MACH,MAAM,aAAa;AAAA,IACpB;AAAA,EACD;AAEA,SAAO;AACR;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport type { View, Filter } from '@wordpress/dataviews';\n\n/**\n * Internal dependencies\n */\nimport type { ActiveViewOverrides } from './types';\n\nconst SCALAR_VALUES = [\n\t'titleField',\n\t'mediaField',\n\t'descriptionField',\n\t'showTitle',\n\t'showMedia',\n\t'showDescription',\n\t'showLevels',\n\t'infiniteScrollEnabled',\n] as const;\n\n/**\n * Merges activeViewOverrides into a view.\n * Filters: Active filters take precedence; same-field filters are replaced.\n * Sort: Active sort is applied only if current sort matches the default.\n *\n * @param view The view to merge overrides into.\n * @param activeViewOverrides The tab-specific overrides to apply.\n * @param defaultView The default view configuration.\n * @return A new view with merged overrides, or the original view if no overrides.\n */\nexport function mergeActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Merge scalar overrides \u2014 always win over persisted values\n\tfor ( const key of SCALAR_VALUES ) {\n\t\tif ( key in activeViewOverrides ) {\n\t\t\tresult = { ...result, [ key ]: activeViewOverrides[ key ] };\n\t\t}\n\t}\n\n\t// Merge filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tconst preserved = ( view.filters ?? [] ).filter(\n\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: [ ...preserved, ...activeViewOverrides.filters ],\n\t\t};\n\t}\n\n\t// Merge sort - only apply if the current sort matches the default\n\tif ( activeViewOverrides.sort ) {\n\t\tconst isDefaultSort =\n\t\t\tdefaultView &&\n\t\t\tview.sort?.field === defaultView.sort?.field &&\n\t\t\tview.sort?.direction === defaultView.sort?.direction;\n\n\t\tif ( isDefaultSort ) {\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tsort: activeViewOverrides.sort,\n\t\t\t};\n\t\t}\n\t}\n\n\t// Merge layout \u2014 shallow merge, override keys always win\n\tif ( activeViewOverrides.layout ) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tlayout: {\n\t\t\t\t...( result as any ).layout,\n\t\t\t\t...activeViewOverrides.layout,\n\t\t\t},\n\t\t} as View;\n\t}\n\n\t// Merge groupBy \u2014 full replacement, override always wins\n\tif ( activeViewOverrides.groupBy ) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tgroupBy: activeViewOverrides.groupBy,\n\t\t};\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips overrides before persisting.\n * Filters: Removes filters on fields managed by activeViewOverrides.\n * Sort: If sort matches the override, restores the default sort.\n *\n * @param view The view to strip overrides from.\n * @param activeViewOverrides The tab-specific override definitions.\n * @param defaultView The default view configuration.\n * @return A new view with overrides stripped, or the original view if no overrides.\n */\nexport function stripActiveViewOverrides(\n\tview: View,\n\tactiveViewOverrides?: ActiveViewOverrides,\n\tdefaultView?: View\n): View {\n\tif ( ! activeViewOverrides ) {\n\t\treturn view;\n\t}\n\n\tlet result = view;\n\n\t// Strip scalar keys managed by overrides\n\tfor ( const key of SCALAR_VALUES ) {\n\t\tif ( key in activeViewOverrides ) {\n\t\t\tconst { [ key ]: _, ...rest } = result;\n\t\t\tresult = rest as View;\n\t\t}\n\t}\n\n\t// Strip managed filters\n\tif (\n\t\tactiveViewOverrides.filters &&\n\t\tactiveViewOverrides.filters.length > 0\n\t) {\n\t\tconst activeFields = new Set(\n\t\t\tactiveViewOverrides.filters.map( ( f ) => f.field )\n\t\t);\n\t\tresult = {\n\t\t\t...result,\n\t\t\tfilters: ( view.filters ?? [] ).filter(\n\t\t\t\t( f: Filter ) => ! activeFields.has( f.field )\n\t\t\t),\n\t\t};\n\t}\n\n\t// Strip sort if it matches the override (restore to default)\n\tif (\n\t\tactiveViewOverrides.sort &&\n\t\tview.sort?.field === activeViewOverrides.sort.field &&\n\t\tview.sort?.direction === activeViewOverrides.sort.direction\n\t) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tsort: defaultView?.sort,\n\t\t};\n\t}\n\n\t// Strip layout keys managed by overrides\n\tif ( activeViewOverrides.layout && 'layout' in result && result.layout ) {\n\t\tconst layout = { ...result.layout } as Record< string, unknown >;\n\t\tfor ( const key of Object.keys( activeViewOverrides.layout ) ) {\n\t\t\tdelete layout[ key ];\n\t\t}\n\t\tresult = {\n\t\t\t...result,\n\t\t\tlayout: Object.keys( layout ).length > 0 ? layout : undefined,\n\t\t} as View;\n\t}\n\n\t// Strip groupBy managed by overrides\n\tif ( activeViewOverrides.groupBy && 'groupBy' in result ) {\n\t\tconst { groupBy: _, ...rest } = result;\n\t\tresult = rest as View;\n\t}\n\n\treturn result;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import type { View, Filter } from '@wordpress/dataviews';
5
- type ActiveViewOverrides = {
6
- filters?: Filter[];
7
- sort?: View['sort'];
8
- };
4
+ import type { View } from '@wordpress/dataviews';
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ import type { ActiveViewOverrides } from './types';
9
9
  /**
10
10
  * Merges activeViewOverrides into a view.
11
11
  * Filters: Active filters take precedence; same-field filters are replaced.
@@ -28,5 +28,4 @@ export declare function mergeActiveViewOverrides(view: View, activeViewOverrides
28
28
  * @return A new view with overrides stripped, or the original view if no overrides.
29
29
  */
30
30
  export declare function stripActiveViewOverrides(view: View, activeViewOverrides?: ActiveViewOverrides, defaultView?: View): View;
31
- export {};
32
31
  //# sourceMappingURL=filter-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter-utils.d.ts","sourceRoot":"","sources":["../src/filter-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEzD,KAAK,mBAAmB,GAAG;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,CAAE,MAAM,CAAE,CAAC;CACtB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CAwCN;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CAoCN"}
1
+ {"version":3,"file":"filter-utils.d.ts","sourceRoot":"","sources":["../src/filter-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAU,MAAM,sBAAsB,CAAC;AAEzD;;GAEG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAanD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CAkEN;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,IAAI,EACV,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,WAAW,CAAC,EAAE,IAAI,GAChB,IAAI,CA8DN"}
@@ -1,7 +1,21 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import type { View, Filter } from '@wordpress/dataviews';
4
+ import type { View } from '@wordpress/dataviews';
5
+ export type ActiveViewOverrides = {
6
+ titleField?: View['titleField'];
7
+ showTitle?: View['showTitle'];
8
+ mediaField?: View['mediaField'];
9
+ showMedia?: View['showMedia'];
10
+ descriptionField?: View['descriptionField'];
11
+ showDescription?: View['showDescription'];
12
+ showLevels?: View['showLevels'];
13
+ infiniteScrollEnabled?: View['infiniteScrollEnabled'];
14
+ filters?: View['filters'];
15
+ sort?: View['sort'];
16
+ groupBy?: View['groupBy'];
17
+ layout?: Record<string, unknown>;
18
+ };
5
19
  export interface ViewConfig {
6
20
  /**
7
21
  * Entity kind (e.g. postType, root).
@@ -23,13 +37,11 @@ export interface ViewConfig {
23
37
  defaultView: View;
24
38
  /**
25
39
  * View overrides applied on top of the persisted view but never persisted.
26
- * These represent tab-specific configuration (filters, sort) that should
27
- * override the persisted view settings.
40
+ * These represent tab-specific configuration (filters, sort) and
41
+ * developer-defined view defaults that should override the persisted
42
+ * view settings.
28
43
  */
29
- activeViewOverrides?: {
30
- filters?: Filter[];
31
- sort?: View['sort'];
32
- };
44
+ activeViewOverrides?: ActiveViewOverrides;
33
45
  /**
34
46
  * Optional query parameters from URL (page, search)
35
47
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,IAAI,CAAE,MAAM,CAAE,CAAC;KACtB,CAAC;IAEF;;OAEG;IACH,WAAW,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAE,MAAM,EAAE;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,KAAM,IAAI,CAAC;CACZ"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG;IAEjC,UAAU,CAAC,EAAE,IAAI,CAAE,YAAY,CAAE,CAAC;IAClC,SAAS,CAAC,EAAE,IAAI,CAAE,WAAW,CAAE,CAAC;IAChC,UAAU,CAAC,EAAE,IAAI,CAAE,YAAY,CAAE,CAAC;IAClC,SAAS,CAAC,EAAE,IAAI,CAAE,WAAW,CAAE,CAAC;IAChC,gBAAgB,CAAC,EAAE,IAAI,CAAE,kBAAkB,CAAE,CAAC;IAC9C,eAAe,CAAC,EAAE,IAAI,CAAE,iBAAiB,CAAE,CAAC;IAC5C,UAAU,CAAC,EAAE,IAAI,CAAE,YAAY,CAAE,CAAC;IAClC,qBAAqB,CAAC,EAAE,IAAI,CAAE,uBAAuB,CAAE,CAAC;IAExD,OAAO,CAAC,EAAE,IAAI,CAAE,SAAS,CAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAE,MAAM,CAAE,CAAC;IACtB,OAAO,CAAC,EAAE,IAAI,CAAE,SAAS,CAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;CACnC,CAAC;AAEF,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAE1C;;OAEG;IACH,WAAW,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAE,MAAM,EAAE;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,KAAM,IAAI,CAAC;CACZ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/views",
3
- "version": "1.7.1-next.v.202602241322.0+bce7cff88",
3
+ "version": "1.8.0",
4
4
  "description": "View persistence and management for WordPress DataViews.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -41,14 +41,14 @@
41
41
  },
42
42
  "types": "build-types",
43
43
  "dependencies": {
44
- "@wordpress/data": "^10.40.1-next.v.202602241322.0+bce7cff88",
45
- "@wordpress/dataviews": "^12.1.1-next.v.202602241322.0+bce7cff88",
46
- "@wordpress/element": "^6.40.1-next.v.202602241322.0+bce7cff88",
47
- "@wordpress/preferences": "^4.40.1-next.v.202602241322.0+bce7cff88",
44
+ "@wordpress/data": "^10.41.0",
45
+ "@wordpress/dataviews": "^13.0.0",
46
+ "@wordpress/element": "^6.41.0",
47
+ "@wordpress/preferences": "^4.41.0",
48
48
  "dequal": "^2.0.3"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
52
52
  },
53
- "gitHead": "943dde7f0b600ce238726c36284bc9f70ce0ffa4"
53
+ "gitHead": "8bfc179b9aed74c0a6dd6e8edf7a49e40e4f87cc"
54
54
  }
@@ -3,10 +3,21 @@
3
3
  */
4
4
  import type { View, Filter } from '@wordpress/dataviews';
5
5
 
6
- type ActiveViewOverrides = {
7
- filters?: Filter[];
8
- sort?: View[ 'sort' ];
9
- };
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type { ActiveViewOverrides } from './types';
10
+
11
+ const SCALAR_VALUES = [
12
+ 'titleField',
13
+ 'mediaField',
14
+ 'descriptionField',
15
+ 'showTitle',
16
+ 'showMedia',
17
+ 'showDescription',
18
+ 'showLevels',
19
+ 'infiniteScrollEnabled',
20
+ ] as const;
10
21
 
11
22
  /**
12
23
  * Merges activeViewOverrides into a view.
@@ -29,6 +40,13 @@ export function mergeActiveViewOverrides(
29
40
 
30
41
  let result = view;
31
42
 
43
+ // Merge scalar overrides — always win over persisted values
44
+ for ( const key of SCALAR_VALUES ) {
45
+ if ( key in activeViewOverrides ) {
46
+ result = { ...result, [ key ]: activeViewOverrides[ key ] };
47
+ }
48
+ }
49
+
32
50
  // Merge filters
33
51
  if (
34
52
  activeViewOverrides.filters &&
@@ -61,6 +79,25 @@ export function mergeActiveViewOverrides(
61
79
  }
62
80
  }
63
81
 
82
+ // Merge layout — shallow merge, override keys always win
83
+ if ( activeViewOverrides.layout ) {
84
+ result = {
85
+ ...result,
86
+ layout: {
87
+ ...( result as any ).layout,
88
+ ...activeViewOverrides.layout,
89
+ },
90
+ } as View;
91
+ }
92
+
93
+ // Merge groupBy — full replacement, override always wins
94
+ if ( activeViewOverrides.groupBy ) {
95
+ result = {
96
+ ...result,
97
+ groupBy: activeViewOverrides.groupBy,
98
+ };
99
+ }
100
+
64
101
  return result;
65
102
  }
66
103
 
@@ -85,6 +122,14 @@ export function stripActiveViewOverrides(
85
122
 
86
123
  let result = view;
87
124
 
125
+ // Strip scalar keys managed by overrides
126
+ for ( const key of SCALAR_VALUES ) {
127
+ if ( key in activeViewOverrides ) {
128
+ const { [ key ]: _, ...rest } = result;
129
+ result = rest as View;
130
+ }
131
+ }
132
+
88
133
  // Strip managed filters
89
134
  if (
90
135
  activeViewOverrides.filters &&
@@ -113,5 +158,23 @@ export function stripActiveViewOverrides(
113
158
  };
114
159
  }
115
160
 
161
+ // Strip layout keys managed by overrides
162
+ if ( activeViewOverrides.layout && 'layout' in result && result.layout ) {
163
+ const layout = { ...result.layout } as Record< string, unknown >;
164
+ for ( const key of Object.keys( activeViewOverrides.layout ) ) {
165
+ delete layout[ key ];
166
+ }
167
+ result = {
168
+ ...result,
169
+ layout: Object.keys( layout ).length > 0 ? layout : undefined,
170
+ } as View;
171
+ }
172
+
173
+ // Strip groupBy managed by overrides
174
+ if ( activeViewOverrides.groupBy && 'groupBy' in result ) {
175
+ const { groupBy: _, ...rest } = result;
176
+ result = rest as View;
177
+ }
178
+
116
179
  return result;
117
180
  }
@@ -0,0 +1,522 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import type { View } from '@wordpress/dataviews';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import {
10
+ mergeActiveViewOverrides,
11
+ stripActiveViewOverrides,
12
+ } from '../filter-utils';
13
+
14
+ const baseView: View = {
15
+ type: 'table',
16
+ filters: [ { field: 'author', operator: 'isAny', value: [ 'admin' ] } ],
17
+ sort: { field: 'date', direction: 'desc' },
18
+ page: 1,
19
+ perPage: 25,
20
+ };
21
+
22
+ const defaultView: View = {
23
+ type: 'table',
24
+ sort: { field: 'date', direction: 'desc' },
25
+ };
26
+
27
+ describe( 'mergeActiveViewOverrides', () => {
28
+ it( 'should return the view unchanged when no overrides are provided', () => {
29
+ expect( mergeActiveViewOverrides( baseView ) ).toBe( baseView );
30
+ expect( mergeActiveViewOverrides( baseView, undefined ) ).toBe(
31
+ baseView
32
+ );
33
+ } );
34
+
35
+ describe( 'scalar overrides', () => {
36
+ it( 'should merge titleField override', () => {
37
+ const result = mergeActiveViewOverrides( baseView, {
38
+ titleField: 'name',
39
+ } );
40
+ expect( result.titleField ).toBe( 'name' );
41
+ } );
42
+
43
+ it( 'should merge mediaField override', () => {
44
+ const result = mergeActiveViewOverrides( baseView, {
45
+ mediaField: 'thumbnail',
46
+ } );
47
+ expect( result.mediaField ).toBe( 'thumbnail' );
48
+ } );
49
+
50
+ it( 'should merge descriptionField override', () => {
51
+ const result = mergeActiveViewOverrides( baseView, {
52
+ descriptionField: 'excerpt',
53
+ } );
54
+ expect( result.descriptionField ).toBe( 'excerpt' );
55
+ } );
56
+
57
+ it( 'should merge showTitle override', () => {
58
+ const result = mergeActiveViewOverrides(
59
+ { ...baseView, showTitle: true },
60
+ { showTitle: false }
61
+ );
62
+ expect( result.showTitle ).toBe( false );
63
+ } );
64
+
65
+ it( 'should merge showMedia override', () => {
66
+ const result = mergeActiveViewOverrides( baseView, {
67
+ showMedia: true,
68
+ } );
69
+ expect( result.showMedia ).toBe( true );
70
+ } );
71
+
72
+ it( 'should merge showDescription override', () => {
73
+ const result = mergeActiveViewOverrides( baseView, {
74
+ showDescription: false,
75
+ } );
76
+ expect( result.showDescription ).toBe( false );
77
+ } );
78
+
79
+ it( 'should merge showLevels override', () => {
80
+ const result = mergeActiveViewOverrides( baseView, {
81
+ showLevels: true,
82
+ } );
83
+ expect( result.showLevels ).toBe( true );
84
+ } );
85
+
86
+ it( 'should merge infiniteScrollEnabled override', () => {
87
+ const result = mergeActiveViewOverrides( baseView, {
88
+ infiniteScrollEnabled: true,
89
+ } );
90
+ expect( result.infiniteScrollEnabled ).toBe( true );
91
+ } );
92
+
93
+ it( 'should override existing scalar value on the view', () => {
94
+ const view: View = { ...baseView, titleField: 'old-title' };
95
+ const result = mergeActiveViewOverrides( view, {
96
+ titleField: 'new-title',
97
+ } );
98
+ expect( result.titleField ).toBe( 'new-title' );
99
+ } );
100
+
101
+ it( 'should merge multiple scalar overrides at once', () => {
102
+ const result = mergeActiveViewOverrides( baseView, {
103
+ titleField: 'name',
104
+ showTitle: true,
105
+ showMedia: false,
106
+ infiniteScrollEnabled: true,
107
+ } );
108
+ expect( result.titleField ).toBe( 'name' );
109
+ expect( result.showTitle ).toBe( true );
110
+ expect( result.showMedia ).toBe( false );
111
+ expect( result.infiniteScrollEnabled ).toBe( true );
112
+ } );
113
+ } );
114
+
115
+ describe( 'filter overrides', () => {
116
+ it( 'should add override filters', () => {
117
+ const result = mergeActiveViewOverrides( baseView, {
118
+ filters: [
119
+ { field: 'status', operator: 'isAny', value: 'publish' },
120
+ ],
121
+ } );
122
+ expect( result.filters ).toHaveLength( 2 );
123
+ expect( result.filters ).toEqual(
124
+ expect.arrayContaining( [
125
+ { field: 'author', operator: 'isAny', value: [ 'admin' ] },
126
+ {
127
+ field: 'status',
128
+ operator: 'isAny',
129
+ value: 'publish',
130
+ },
131
+ ] )
132
+ );
133
+ } );
134
+
135
+ it( 'should replace same-field filters', () => {
136
+ const result = mergeActiveViewOverrides( baseView, {
137
+ filters: [
138
+ {
139
+ field: 'author',
140
+ operator: 'isAny',
141
+ value: [ 'editor' ],
142
+ },
143
+ ],
144
+ } );
145
+ expect( result.filters ).toHaveLength( 1 );
146
+ expect( result.filters![ 0 ] ).toEqual( {
147
+ field: 'author',
148
+ operator: 'isAny',
149
+ value: [ 'editor' ],
150
+ } );
151
+ } );
152
+
153
+ it( 'should handle empty override filters array', () => {
154
+ const result = mergeActiveViewOverrides( baseView, {
155
+ filters: [],
156
+ } );
157
+ // Empty filters array is treated as no override.
158
+ expect( result.filters ).toEqual( baseView.filters );
159
+ } );
160
+ } );
161
+
162
+ describe( 'sort overrides', () => {
163
+ it( 'should apply sort override when current sort matches default', () => {
164
+ const result = mergeActiveViewOverrides(
165
+ baseView,
166
+ { sort: { field: 'title', direction: 'asc' } },
167
+ defaultView
168
+ );
169
+ expect( result.sort ).toEqual( {
170
+ field: 'title',
171
+ direction: 'asc',
172
+ } );
173
+ } );
174
+
175
+ it( 'should not apply sort override when user has changed sort', () => {
176
+ const userView: View = {
177
+ ...baseView,
178
+ sort: { field: 'title', direction: 'asc' },
179
+ };
180
+ const result = mergeActiveViewOverrides(
181
+ userView,
182
+ { sort: { field: 'modified', direction: 'desc' } },
183
+ defaultView
184
+ );
185
+ expect( result.sort ).toEqual( {
186
+ field: 'title',
187
+ direction: 'asc',
188
+ } );
189
+ } );
190
+
191
+ it( 'should not apply sort override when no default view is provided', () => {
192
+ const result = mergeActiveViewOverrides( baseView, {
193
+ sort: { field: 'title', direction: 'asc' },
194
+ } );
195
+ expect( result.sort ).toEqual( baseView.sort );
196
+ } );
197
+ } );
198
+
199
+ describe( 'layout overrides', () => {
200
+ it( 'should merge layout override into existing layout', () => {
201
+ const view: View = {
202
+ ...baseView,
203
+ layout: { density: 'compact' },
204
+ };
205
+ const result = mergeActiveViewOverrides( view, {
206
+ layout: { styles: { author: { align: 'end' } } },
207
+ } );
208
+ expect( result.layout ).toEqual( {
209
+ density: 'compact',
210
+ styles: { author: { align: 'end' } },
211
+ } );
212
+ } );
213
+
214
+ it( 'should set layout when view has no existing layout', () => {
215
+ const result = mergeActiveViewOverrides( baseView, {
216
+ layout: { styles: { title: { width: '50%' } } },
217
+ } );
218
+ expect( result.layout ).toEqual( {
219
+ styles: { title: { width: '50%' } },
220
+ } );
221
+ } );
222
+
223
+ it( 'should override matching layout keys', () => {
224
+ const view: View = {
225
+ ...baseView,
226
+ layout: {
227
+ density: 'compact',
228
+ styles: { old: { width: '10%' } },
229
+ },
230
+ };
231
+ const result = mergeActiveViewOverrides( view, {
232
+ layout: { styles: { new: { width: '20%' } } },
233
+ } );
234
+ // Shallow merge: styles key is replaced entirely.
235
+ expect( result.layout ).toEqual( {
236
+ density: 'compact',
237
+ styles: { new: { width: '20%' } },
238
+ } );
239
+ } );
240
+ } );
241
+
242
+ describe( 'groupBy overrides', () => {
243
+ it( 'should replace groupBy with override', () => {
244
+ const view: View = {
245
+ ...baseView,
246
+ groupBy: {
247
+ field: 'status',
248
+ direction: 'asc',
249
+ showLabel: false,
250
+ },
251
+ };
252
+ const result = mergeActiveViewOverrides( view, {
253
+ groupBy: { field: 'category', direction: 'desc' },
254
+ } );
255
+ expect( result.groupBy ).toEqual( {
256
+ field: 'category',
257
+ direction: 'desc',
258
+ } );
259
+ } );
260
+
261
+ it( 'should set groupBy when view has none', () => {
262
+ const result = mergeActiveViewOverrides( baseView, {
263
+ groupBy: { field: 'category', direction: 'desc' },
264
+ } );
265
+ expect( result.groupBy ).toEqual( {
266
+ field: 'category',
267
+ direction: 'desc',
268
+ } );
269
+ } );
270
+ } );
271
+
272
+ it( 'should not mutate the original view', () => {
273
+ const original = { ...baseView };
274
+ mergeActiveViewOverrides( original, {
275
+ titleField: 'name',
276
+ filters: [
277
+ { field: 'status', operator: 'isAny', value: 'publish' },
278
+ ],
279
+ layout: { styles: {} },
280
+ } );
281
+ expect( original ).toEqual( baseView );
282
+ } );
283
+ } );
284
+
285
+ describe( 'stripActiveViewOverrides', () => {
286
+ it( 'should return the view unchanged when no overrides are provided', () => {
287
+ expect( stripActiveViewOverrides( baseView ) ).toBe( baseView );
288
+ expect( stripActiveViewOverrides( baseView, undefined ) ).toBe(
289
+ baseView
290
+ );
291
+ } );
292
+
293
+ describe( 'scalar stripping', () => {
294
+ it( 'should strip a scalar key managed by overrides', () => {
295
+ const view: View = { ...baseView, titleField: 'name' };
296
+ const result = stripActiveViewOverrides( view, {
297
+ titleField: 'name',
298
+ } );
299
+ expect( result ).not.toHaveProperty( 'titleField' );
300
+ } );
301
+
302
+ it( 'should strip multiple scalar keys', () => {
303
+ const view: View = {
304
+ ...baseView,
305
+ titleField: 'name',
306
+ showTitle: true,
307
+ mediaField: 'thumb',
308
+ };
309
+ const result = stripActiveViewOverrides( view, {
310
+ titleField: 'name',
311
+ showTitle: true,
312
+ mediaField: 'thumb',
313
+ } );
314
+ expect( result ).not.toHaveProperty( 'titleField' );
315
+ expect( result ).not.toHaveProperty( 'showTitle' );
316
+ expect( result ).not.toHaveProperty( 'mediaField' );
317
+ } );
318
+
319
+ it( 'should preserve non-overridden scalar keys', () => {
320
+ const view: View = {
321
+ ...baseView,
322
+ titleField: 'name',
323
+ descriptionField: 'excerpt',
324
+ };
325
+ const result = stripActiveViewOverrides( view, {
326
+ titleField: 'name',
327
+ } );
328
+ expect( result ).not.toHaveProperty( 'titleField' );
329
+ expect( result.descriptionField ).toBe( 'excerpt' );
330
+ } );
331
+ } );
332
+
333
+ describe( 'filter stripping', () => {
334
+ it( 'should remove filters on managed fields', () => {
335
+ const view: View = {
336
+ ...baseView,
337
+ filters: [
338
+ {
339
+ field: 'status',
340
+ operator: 'isAny',
341
+ value: 'publish',
342
+ },
343
+ {
344
+ field: 'author',
345
+ operator: 'isAny',
346
+ value: [ 'admin' ],
347
+ },
348
+ ],
349
+ };
350
+ const result = stripActiveViewOverrides( view, {
351
+ filters: [
352
+ {
353
+ field: 'status',
354
+ operator: 'isAny',
355
+ value: 'publish',
356
+ },
357
+ ],
358
+ } );
359
+ expect( result.filters ).toHaveLength( 1 );
360
+ expect( result.filters?.[ 0 ].field ).toBe( 'author' );
361
+ } );
362
+
363
+ it( 'should handle empty override filters', () => {
364
+ const result = stripActiveViewOverrides( baseView, {
365
+ filters: [],
366
+ } );
367
+ expect( result.filters ).toEqual( baseView.filters );
368
+ } );
369
+ } );
370
+
371
+ describe( 'sort stripping', () => {
372
+ it( 'should restore default sort when current matches override', () => {
373
+ const view: View = {
374
+ ...baseView,
375
+ sort: { field: 'title', direction: 'asc' },
376
+ };
377
+ const result = stripActiveViewOverrides(
378
+ view,
379
+ { sort: { field: 'title', direction: 'asc' } },
380
+ defaultView
381
+ );
382
+ expect( result.sort ).toEqual( defaultView.sort );
383
+ } );
384
+
385
+ it( 'should not change sort when it does not match override', () => {
386
+ const view: View = {
387
+ ...baseView,
388
+ sort: { field: 'author', direction: 'asc' },
389
+ };
390
+ const result = stripActiveViewOverrides(
391
+ view,
392
+ { sort: { field: 'title', direction: 'asc' } },
393
+ defaultView
394
+ );
395
+ expect( result.sort ).toEqual( {
396
+ field: 'author',
397
+ direction: 'asc',
398
+ } );
399
+ } );
400
+ } );
401
+
402
+ describe( 'layout stripping', () => {
403
+ it( 'should strip layout keys managed by overrides', () => {
404
+ const view: View = {
405
+ ...baseView,
406
+ layout: {
407
+ density: 'compact',
408
+ styles: { author: { align: 'end' } },
409
+ },
410
+ };
411
+ const result = stripActiveViewOverrides( view, {
412
+ layout: { styles: { author: { align: 'end' } } },
413
+ } );
414
+ expect( result.layout ).toEqual( { density: 'compact' } );
415
+ } );
416
+
417
+ it( 'should set layout to undefined when all keys are stripped', () => {
418
+ const view: View = {
419
+ ...baseView,
420
+ layout: {
421
+ styles: { author: { align: 'end' } },
422
+ },
423
+ };
424
+ const result = stripActiveViewOverrides( view, {
425
+ layout: { styles: { author: { align: 'end' } } },
426
+ } );
427
+ expect( result.layout ).toBeUndefined();
428
+ } );
429
+
430
+ it( 'should not touch layout when view has no layout', () => {
431
+ const result = stripActiveViewOverrides( baseView, {
432
+ layout: { styles: {} },
433
+ } );
434
+ expect( result ).not.toHaveProperty( 'layout' );
435
+ } );
436
+ } );
437
+
438
+ describe( 'groupBy stripping', () => {
439
+ it( 'should remove groupBy when managed by overrides', () => {
440
+ const view: View = {
441
+ ...baseView,
442
+ groupBy: {
443
+ field: 'status',
444
+ direction: 'asc',
445
+ showLabel: true,
446
+ },
447
+ };
448
+ const result = stripActiveViewOverrides( view, {
449
+ groupBy: { field: 'category', direction: 'desc' },
450
+ } );
451
+ expect( result ).not.toHaveProperty( 'groupBy' );
452
+ } );
453
+
454
+ it( 'should not touch groupBy when view has no groupBy', () => {
455
+ const result = stripActiveViewOverrides( baseView, {
456
+ groupBy: { field: 'category', direction: 'asc' },
457
+ } );
458
+ expect( result ).not.toHaveProperty( 'groupBy' );
459
+ } );
460
+ } );
461
+
462
+ it( 'should not mutate the original view', () => {
463
+ const view: View = {
464
+ ...baseView,
465
+ titleField: 'name',
466
+ layout: { density: 'compact', styles: { a: { width: '1px' } } },
467
+ };
468
+ const original = { ...view, layout: { ...view.layout } };
469
+ stripActiveViewOverrides( view, {
470
+ titleField: 'name',
471
+ layout: { styles: {} },
472
+ } );
473
+ expect( view ).toEqual( original );
474
+ } );
475
+ } );
476
+
477
+ describe( 'merge + strip round-trip', () => {
478
+ it( 'should strip what merge added for scalar overrides', () => {
479
+ const overrides = {
480
+ titleField: 'name' as const,
481
+ showMedia: true as const,
482
+ };
483
+ const merged = mergeActiveViewOverrides( baseView, overrides );
484
+ const stripped = stripActiveViewOverrides( merged, overrides );
485
+ expect( stripped ).not.toHaveProperty( 'titleField' );
486
+ expect( stripped ).not.toHaveProperty( 'showMedia' );
487
+ // Original fields remain.
488
+ expect( stripped.type ).toBe( 'table' );
489
+ expect( stripped.sort ).toEqual( baseView.sort );
490
+ } );
491
+
492
+ it( 'should strip what merge added for layout overrides', () => {
493
+ const overrides = {
494
+ layout: { styles: { author: { align: 'end' } } },
495
+ };
496
+ const view: View = {
497
+ ...baseView,
498
+ layout: { density: 'compact' },
499
+ };
500
+ const merged = mergeActiveViewOverrides( view, overrides );
501
+ const stripped = stripActiveViewOverrides( merged, overrides );
502
+ expect( stripped.layout ).toEqual( { density: 'compact' } );
503
+ } );
504
+
505
+ it( 'should strip what merge added for filter overrides', () => {
506
+ const overrides = {
507
+ filters: [
508
+ {
509
+ field: 'status' as const,
510
+ operator: 'isAny' as const,
511
+ value: 'publish',
512
+ },
513
+ ],
514
+ };
515
+ const merged = mergeActiveViewOverrides( baseView, overrides );
516
+ const stripped = stripActiveViewOverrides( merged, overrides );
517
+ // Only the original author filter should remain.
518
+ expect( stripped.filters ).toEqual( [
519
+ { field: 'author', operator: 'isAny', value: [ 'admin' ] },
520
+ ] );
521
+ } );
522
+ } );
package/src/types.ts CHANGED
@@ -1,7 +1,24 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import type { View, Filter } from '@wordpress/dataviews';
4
+ import type { View } from '@wordpress/dataviews';
5
+
6
+ export type ActiveViewOverrides = {
7
+ // scalar values
8
+ titleField?: View[ 'titleField' ];
9
+ showTitle?: View[ 'showTitle' ];
10
+ mediaField?: View[ 'mediaField' ];
11
+ showMedia?: View[ 'showMedia' ];
12
+ descriptionField?: View[ 'descriptionField' ];
13
+ showDescription?: View[ 'showDescription' ];
14
+ showLevels?: View[ 'showLevels' ];
15
+ infiniteScrollEnabled?: View[ 'infiniteScrollEnabled' ];
16
+ // array & object values
17
+ filters?: View[ 'filters' ];
18
+ sort?: View[ 'sort' ];
19
+ groupBy?: View[ 'groupBy' ];
20
+ layout?: Record< string, unknown >;
21
+ };
5
22
 
6
23
  export interface ViewConfig {
7
24
  /**
@@ -28,13 +45,11 @@ export interface ViewConfig {
28
45
 
29
46
  /**
30
47
  * View overrides applied on top of the persisted view but never persisted.
31
- * These represent tab-specific configuration (filters, sort) that should
32
- * override the persisted view settings.
48
+ * These represent tab-specific configuration (filters, sort) and
49
+ * developer-defined view defaults that should override the persisted
50
+ * view settings.
33
51
  */
34
- activeViewOverrides?: {
35
- filters?: Filter[];
36
- sort?: View[ 'sort' ];
37
- };
52
+ activeViewOverrides?: ActiveViewOverrides;
38
53
 
39
54
  /**
40
55
  * Optional query parameters from URL (page, search)