getPanelOptionsWithDefaults.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. import { mockStandardFieldConfigOptions } from 'test/helpers/fieldConfig';
  2. import {
  3. ConfigOverrideRule,
  4. FieldColorModeId,
  5. FieldConfig,
  6. FieldConfigProperty,
  7. FieldConfigSource,
  8. PanelPlugin,
  9. standardEditorsRegistry,
  10. standardFieldConfigEditorRegistry,
  11. StandardOptionConfig,
  12. ThresholdsMode,
  13. } from '@grafana/data';
  14. import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks';
  15. import { getPanelOptionsWithDefaults, restoreCustomOverrideRules } from './getPanelOptionsWithDefaults';
  16. standardFieldConfigEditorRegistry.setInit(() => mockStandardFieldConfigOptions());
  17. standardEditorsRegistry.setInit(() => mockStandardFieldConfigOptions());
  18. const pluginA = getPanelPlugin({ id: 'graph' });
  19. pluginA.useFieldConfig({
  20. useCustomConfig: (builder) => {
  21. builder.addBooleanSwitch({
  22. name: 'Hide lines',
  23. path: 'hideLines',
  24. defaultValue: false,
  25. });
  26. },
  27. });
  28. pluginA.setPanelOptions((builder) => {
  29. builder.addBooleanSwitch({
  30. name: 'Show thresholds',
  31. path: 'showThresholds',
  32. defaultValue: true,
  33. });
  34. builder.addTextInput({
  35. name: 'Name',
  36. path: 'name',
  37. defaultValue: 'hello',
  38. });
  39. builder.addNumberInput({
  40. name: 'Number',
  41. path: 'number',
  42. defaultValue: 10,
  43. });
  44. });
  45. describe('getPanelOptionsWithDefaults', () => {
  46. describe('When panel plugin has no options', () => {
  47. it('Should set defaults', () => {
  48. const result = runScenario({
  49. plugin: getPanelPlugin({ id: 'graph' }),
  50. options: {},
  51. defaults: {},
  52. overrides: [],
  53. });
  54. expect(result).toMatchInlineSnapshot(`
  55. Object {
  56. "fieldConfig": Object {
  57. "defaults": Object {},
  58. "overrides": Array [],
  59. },
  60. "options": Object {},
  61. }
  62. `);
  63. });
  64. });
  65. describe('When current options are emtpy', () => {
  66. it('Should set defaults', () => {
  67. const result = getPanelOptionsWithDefaults({
  68. plugin: pluginA,
  69. currentOptions: {},
  70. currentFieldConfig: {
  71. defaults: {},
  72. overrides: [],
  73. },
  74. isAfterPluginChange: false,
  75. });
  76. expect(result).toMatchInlineSnapshot(`
  77. Object {
  78. "fieldConfig": Object {
  79. "defaults": Object {
  80. "custom": Object {
  81. "hideLines": false,
  82. },
  83. "thresholds": Object {
  84. "mode": "absolute",
  85. "steps": Array [
  86. Object {
  87. "color": "green",
  88. "value": -Infinity,
  89. },
  90. Object {
  91. "color": "red",
  92. "value": 80,
  93. },
  94. ],
  95. },
  96. },
  97. "overrides": Array [],
  98. },
  99. "options": Object {
  100. "name": "hello",
  101. "number": 10,
  102. "showThresholds": true,
  103. },
  104. }
  105. `);
  106. });
  107. });
  108. describe('When there are current options and overrides', () => {
  109. it('Should set defaults', () => {
  110. const result = getPanelOptionsWithDefaults({
  111. plugin: pluginA,
  112. currentOptions: {
  113. number: 20,
  114. showThresholds: false,
  115. },
  116. currentFieldConfig: {
  117. defaults: {
  118. unit: 'bytes',
  119. decimals: 2,
  120. },
  121. overrides: [],
  122. },
  123. isAfterPluginChange: true,
  124. });
  125. expect(result).toMatchInlineSnapshot(`
  126. Object {
  127. "fieldConfig": Object {
  128. "defaults": Object {
  129. "custom": Object {
  130. "hideLines": false,
  131. },
  132. "decimals": 2,
  133. "thresholds": Object {
  134. "mode": "absolute",
  135. "steps": Array [
  136. Object {
  137. "color": "green",
  138. "value": -Infinity,
  139. },
  140. Object {
  141. "color": "red",
  142. "value": 80,
  143. },
  144. ],
  145. },
  146. "unit": "bytes",
  147. },
  148. "overrides": Array [],
  149. },
  150. "options": Object {
  151. "name": "hello",
  152. "number": 20,
  153. "showThresholds": false,
  154. },
  155. }
  156. `);
  157. });
  158. });
  159. describe('when changing panel type to one that does not support by value color mode', () => {
  160. it('should change color mode', () => {
  161. const plugin = getPanelPlugin({ id: 'graph' }).useFieldConfig({
  162. standardOptions: {
  163. [FieldConfigProperty.Color]: {
  164. settings: {
  165. byValueSupport: false,
  166. },
  167. },
  168. },
  169. });
  170. const result = getPanelOptionsWithDefaults({
  171. plugin,
  172. currentOptions: {},
  173. currentFieldConfig: {
  174. defaults: {
  175. color: { mode: FieldColorModeId.Thresholds },
  176. },
  177. overrides: [],
  178. },
  179. isAfterPluginChange: true,
  180. });
  181. expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
  182. });
  183. });
  184. describe('when changing panel type from one not supporting by value color mode to one that supports it', () => {
  185. it('should keep supported mode', () => {
  186. const result = runScenario({
  187. defaults: {
  188. color: { mode: FieldColorModeId.PaletteClassic },
  189. },
  190. standardOptions: {
  191. [FieldConfigProperty.Color]: {
  192. settings: {
  193. byValueSupport: true,
  194. },
  195. },
  196. },
  197. });
  198. expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
  199. });
  200. it('should change to thresholds mode when it prefers to', () => {
  201. const result = runScenario({
  202. defaults: {
  203. color: { mode: FieldColorModeId.PaletteClassic },
  204. },
  205. standardOptions: {
  206. [FieldConfigProperty.Color]: {
  207. settings: {
  208. byValueSupport: true,
  209. preferThresholdsMode: true,
  210. },
  211. },
  212. },
  213. isAfterPluginChange: true,
  214. });
  215. expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.Thresholds);
  216. });
  217. it('should change to classic mode when panel supports bySeries', () => {
  218. const result = runScenario({
  219. defaults: {
  220. color: { mode: FieldColorModeId.Thresholds },
  221. },
  222. standardOptions: {
  223. [FieldConfigProperty.Color]: {
  224. settings: {
  225. byValueSupport: true,
  226. bySeriesSupport: true,
  227. },
  228. },
  229. },
  230. isAfterPluginChange: true,
  231. });
  232. expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
  233. });
  234. });
  235. describe('when changing panel type to one that does not use standard field config', () => {
  236. it('should clean defaults', () => {
  237. const plugin = getPanelPlugin({ id: 'graph' });
  238. const result = getPanelOptionsWithDefaults({
  239. plugin,
  240. currentOptions: {},
  241. currentFieldConfig: {
  242. defaults: {
  243. color: { mode: FieldColorModeId.Thresholds },
  244. thresholds: {
  245. mode: ThresholdsMode.Absolute,
  246. steps: [],
  247. },
  248. },
  249. overrides: [],
  250. },
  251. isAfterPluginChange: true,
  252. });
  253. expect(result.fieldConfig.defaults.thresholds).toBeUndefined();
  254. });
  255. });
  256. describe('when applying defaults clean properties that are no longer part of the registry', () => {
  257. it('should remove custom defaults that no longer exist', () => {
  258. const result = runScenario({
  259. defaults: {
  260. unit: 'bytes',
  261. custom: {
  262. customProp: 20,
  263. customPropNoExist: true,
  264. nested: {
  265. nestedA: 'A',
  266. nestedB: 'B',
  267. },
  268. },
  269. },
  270. });
  271. expect(result.fieldConfig.defaults).toMatchInlineSnapshot(`
  272. Object {
  273. "custom": Object {
  274. "customProp": 20,
  275. "nested": Object {
  276. "nestedA": "A",
  277. },
  278. },
  279. "thresholds": Object {
  280. "mode": "absolute",
  281. "steps": Array [
  282. Object {
  283. "color": "green",
  284. "value": -Infinity,
  285. },
  286. Object {
  287. "color": "red",
  288. "value": 80,
  289. },
  290. ],
  291. },
  292. "unit": "bytes",
  293. }
  294. `);
  295. });
  296. it('should remove custom overrides that no longer exist', () => {
  297. const result = runScenario({
  298. defaults: {},
  299. overrides: [
  300. {
  301. matcher: { id: 'byName', options: 'D-series' },
  302. properties: [
  303. {
  304. id: 'custom.customPropNoExist',
  305. value: 'google',
  306. },
  307. ],
  308. },
  309. {
  310. matcher: { id: 'byName', options: 'D-series' },
  311. properties: [
  312. {
  313. id: 'custom.customProp',
  314. value: 30,
  315. },
  316. ],
  317. },
  318. ],
  319. });
  320. expect(result.fieldConfig.overrides.length).toBe(1);
  321. expect(result.fieldConfig.overrides[0].properties[0].id).toBe('custom.customProp');
  322. });
  323. });
  324. });
  325. describe('restoreCustomOverrideRules', () => {
  326. it('should add back custom rules', () => {
  327. const current = {
  328. defaults: {},
  329. overrides: [
  330. {
  331. matcher: { id: 'byName', options: 'SeriesA' },
  332. properties: [
  333. {
  334. id: 'decimals',
  335. value: 2,
  336. },
  337. ],
  338. },
  339. ],
  340. };
  341. const old = {
  342. defaults: {},
  343. overrides: [
  344. {
  345. matcher: { id: 'byName', options: 'SeriesA' },
  346. properties: [
  347. {
  348. id: 'custom.propName',
  349. value: 10,
  350. },
  351. ],
  352. },
  353. {
  354. matcher: { id: 'byName', options: 'SeriesB' },
  355. properties: [
  356. {
  357. id: 'custom.propName',
  358. value: 20,
  359. },
  360. ],
  361. },
  362. ],
  363. };
  364. const result = restoreCustomOverrideRules(current, old);
  365. expect(result.overrides.length).toBe(2);
  366. expect(result.overrides[0].properties[0].id).toBe('decimals');
  367. expect(result.overrides[0].properties[1].id).toBe('custom.propName');
  368. expect(result.overrides[1].properties.length).toBe(1);
  369. expect(result.overrides[1].matcher.options).toBe('SeriesB');
  370. });
  371. });
  372. interface ScenarioOptions {
  373. defaults?: FieldConfig<any>;
  374. overrides?: ConfigOverrideRule[];
  375. disabledStandardOptions?: FieldConfigProperty[];
  376. standardOptions?: Partial<Record<FieldConfigProperty, StandardOptionConfig>>;
  377. plugin?: PanelPlugin;
  378. options?: any;
  379. isAfterPluginChange?: boolean;
  380. }
  381. function runScenario(options: ScenarioOptions) {
  382. const fieldConfig: FieldConfigSource = {
  383. defaults: options.defaults || {},
  384. overrides: options.overrides || [],
  385. };
  386. const plugin =
  387. options.plugin ??
  388. getPanelPlugin({ id: 'graph' }).useFieldConfig({
  389. standardOptions: options.standardOptions,
  390. useCustomConfig: (builder) => {
  391. builder.addNumberInput({
  392. name: 'Custom prop',
  393. path: 'customProp',
  394. defaultValue: 10,
  395. });
  396. builder.addTextInput({
  397. name: 'Nested prop',
  398. path: 'nested.nestedA',
  399. });
  400. },
  401. });
  402. return getPanelOptionsWithDefaults({
  403. plugin,
  404. currentOptions: options.options || {},
  405. currentFieldConfig: fieldConfig,
  406. isAfterPluginChange: !!options.isAfterPluginChange,
  407. });
  408. }