@c8y/ngx-components 1024.16.17 → 1024.16.19

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.
@@ -350,10 +350,9 @@ class DataFetchingService {
350
350
  * @returns A map where the key is the data point ID and the value is an array of data point series.
351
351
  */
352
352
  groupSeriesByDeviceId(datapointDetails) {
353
- return datapointDetails.reduce((map, { valueFragmentType, valueFragmentSeries, source }) => {
354
- const value = `${valueFragmentType}.${valueFragmentSeries}`;
355
- const existingValue = map.get(source) ?? [];
356
- map.set(source, [...existingValue, value]);
353
+ return datapointDetails.reduce((map, datapoint) => {
354
+ const existingValue = map.get(datapoint.source) ?? [];
355
+ map.set(datapoint.source, [...existingValue, this.getSeriesName(datapoint)]);
357
356
  return map;
358
357
  }, new Map());
359
358
  }
@@ -373,7 +372,7 @@ class DataFetchingService {
373
372
  * Unique key to distinguish between different series from same source,
374
373
  * e.g.: c8y_Acceleration.accelerationX, c8y_Acceleration.accelerationY, c8y_Acceleration.accelerationZ
375
374
  */
376
- const seriesKey = `${details.valueFragmentType}.${details.valueFragmentSeries}`;
375
+ const seriesKey = this.getSeriesName(details);
377
376
  if (valuesGroupedBySource[details.source][seriesKey]) {
378
377
  unit = valuesGroupedBySource[details.source][seriesKey].seriesDetails.unit;
379
378
  data = valuesGroupedBySource[details.source][seriesKey].values;
@@ -409,29 +408,71 @@ class DataFetchingService {
409
408
  return false;
410
409
  }
411
410
  }
411
+ /**
412
+ * Returns sources of the given data points that the user is permitted to read,
413
+ * one entry per permitted data point.
414
+ *
415
+ * @deprecated Use `getDatapointsWithPermissionsToRead` instead — permissions can differ
416
+ * between data points of the same source (inventory role permissions are scoped per fragment type).
417
+ * @param datapointDetails - The data points selected for export.
418
+ * @returns A promise that resolves to the sources of the permitted data points.
419
+ */
412
420
  async getSourcesWithPermissionsToRead(datapointDetails) {
413
- const dateFrom = new Date();
414
- const dateTo = new Date(dateFrom);
415
- const fetchedDataPromises = datapointDetails.map(async (datapoint) => {
416
- const rawFilter = {
417
- dateFrom,
418
- dateTo,
419
- source: datapoint.source
420
- };
421
- try {
422
- const { res } = await this.fetchSeriesData(rawFilter);
423
- if (res?.status === 200) {
424
- return datapoint.source;
425
- }
426
- return null;
427
- }
428
- catch (error) {
429
- // If an error occurs during fetch, return null to prevent promise rejection
430
- return null;
431
- }
432
- });
433
- const fetchedSources = await Promise.all(fetchedDataPromises);
434
- return fetchedSources.filter((source) => source !== null);
421
+ const permittedDatapoints = await this.getDatapointsWithPermissionsToRead(datapointDetails);
422
+ return permittedDatapoints.map(({ source }) => String(source));
423
+ }
424
+ /**
425
+ * Returns the subset of the given data points that the user is permitted to read.
426
+ *
427
+ * Each probe request lists the exact series to export, as required for measurement READ
428
+ * permissions scoped to specific fragment types. When a probe grouping all series of a source
429
+ * is rejected, each series is probed individually, so readable data points are not blocked
430
+ * by unreadable ones.
431
+ *
432
+ * Example flow for data points `Cloud.speed` and `c8y_Battery.level` on source `123` plus
433
+ * `Cloud.level` on source `456`, with an inventory role permitting only the `Cloud` fragment:
434
+ * ```
435
+ * GET /series?source=123&series=Cloud.speed&series=c8y_Battery.level → 403, probe each series:
436
+ * GET /series?source=123&series=Cloud.speed → 200 (readable)
437
+ * GET /series?source=123&series=c8y_Battery.level → 403 (dropped)
438
+ * GET /series?source=456&series=Cloud.level → 200 (readable)
439
+ *
440
+ * Returns the data points of Cloud.speed and Cloud.level.
441
+ * ```
442
+ *
443
+ * @param datapointDetails - The data points selected for export.
444
+ * @returns A promise that resolves to the data points the user is permitted to read.
445
+ */
446
+ async getDatapointsWithPermissionsToRead(datapointDetails) {
447
+ const datapointsBySource = this.groupDatapointsBySource(datapointDetails);
448
+ const readableGroups = await Promise.all(Array.from(datapointsBySource.values(), datapoints => this.getReadableDatapointsOfSource(datapoints)));
449
+ const readableDatapoints = new Set(readableGroups.flat());
450
+ // Filter the original array to keep the caller's data point order.
451
+ return datapointDetails.filter(datapoint => readableDatapoints.has(datapoint));
452
+ }
453
+ /**
454
+ * Fetches series data for export while deriving read permissions from the responses,
455
+ * so no separate permission probes are needed for readable sources.
456
+ *
457
+ * The data request itself acts as the permission check: when a source group is rejected
458
+ * with 403, each series is probed individually and only the readable ones are refetched.
459
+ *
460
+ * @param exportConfig - The export configuration providing the data points, date range and aggregation.
461
+ * @returns A promise that resolves to the prepared export data and the readable data points.
462
+ */
463
+ async fetchAndPrepareSeriesDataWithPermissions(exportConfig) {
464
+ const datapointsBySource = this.groupDatapointsBySource(exportConfig.datapointDetails);
465
+ const groupResults = await Promise.all(Array.from(datapointsBySource.values(), datapoints => this.fetchReadableSeriesOfSource(datapoints, exportConfig)));
466
+ const readableSet = new Set(groupResults.flatMap(result => result.readableDatapoints));
467
+ // Filter the original array to keep the caller's data point order.
468
+ const readableDatapoints = exportConfig.datapointDetails.filter(datapoint => readableSet.has(datapoint));
469
+ const fetchedDataGroupedBySource = groupResults
470
+ .map(result => result.sourceItem)
471
+ .filter(sourceItem => sourceItem !== null);
472
+ return {
473
+ dataToExport: this.processSeriesData(readableDatapoints, fetchedDataGroupedBySource),
474
+ readableDatapoints
475
+ };
435
476
  }
436
477
  /**
437
478
  * Adjusts the given date by adding the specified number of minutes and setting seconds to 0.
@@ -529,7 +570,7 @@ class DataFetchingService {
529
570
  const diffPercent = target !== null && target !== undefined && diff !== null ? (diff / target) * 100 : null;
530
571
  return {
531
572
  ...detail,
532
- label: detail.label || `${detail.valueFragmentType}.${detail.valueFragmentSeries}`,
573
+ label: detail.label || this.getSeriesName(detail),
533
574
  target: target ?? null,
534
575
  current,
535
576
  diff,
@@ -537,10 +578,10 @@ class DataFetchingService {
537
578
  unit
538
579
  };
539
580
  }
540
- catch (error) {
581
+ catch {
541
582
  return {
542
583
  ...detail,
543
- label: detail.label || `${detail.valueFragmentType}.${detail.valueFragmentSeries}`,
584
+ label: detail.label || this.getSeriesName(detail),
544
585
  target: detail.target ?? null,
545
586
  current: null,
546
587
  diff: null,
@@ -583,21 +624,133 @@ class DataFetchingService {
583
624
  return measurements;
584
625
  }
585
626
  async fetchAndPrepareSeriesDataToExport(exportConfig) {
586
- const datapointsValuesDataMap = this.groupSeriesByDeviceId(exportConfig.datapointDetails);
587
- const fetchedDataPromises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => {
588
- const { dateFrom, dateTo, aggregation } = exportConfig;
589
- const rawFilter = {
590
- dateFrom,
591
- dateTo,
592
- source,
593
- series,
594
- aggregationType: aggregation
627
+ const { dataToExport } = await this.fetchAndPrepareSeriesDataWithPermissions(exportConfig);
628
+ return dataToExport;
629
+ }
630
+ /**
631
+ * Groups the given data points by their source.
632
+ *
633
+ * @param datapointDetails - The data points to group.
634
+ * @returns A map where the key is the source ID and the value is the data points of that source.
635
+ */
636
+ groupDatapointsBySource(datapointDetails) {
637
+ return datapointDetails.reduce((map, datapoint) => {
638
+ const group = map.get(datapoint.source) ?? [];
639
+ map.set(datapoint.source, [...group, datapoint]);
640
+ return map;
641
+ }, new Map());
642
+ }
643
+ /**
644
+ * Returns the data points of a single source that the user is permitted to read.
645
+ * Starts with one probe covering all series of the source; when that probe is rejected,
646
+ * each series is probed individually, so readable data points are not blocked by unreadable ones.
647
+ *
648
+ * @param datapoints - The data points of one source.
649
+ * @returns A promise that resolves to the readable data points.
650
+ */
651
+ async getReadableDatapointsOfSource(datapoints) {
652
+ const source = datapoints[0].source;
653
+ const allSeries = datapoints.map(datapoint => this.getSeriesName(datapoint));
654
+ if (await this.isSeriesReadable(source, allSeries)) {
655
+ return datapoints;
656
+ }
657
+ return datapoints.length === 1 ? [] : this.filterReadableDatapoints(datapoints);
658
+ }
659
+ /**
660
+ * Fetches the series data of a single source, deriving which of its data points are readable.
661
+ * A 403 for the grouped request triggers per-series probes and a refetch of the readable series;
662
+ * any other failure yields no readable data points, matching a rejected permission probe.
663
+ *
664
+ * @param datapoints - The data points of one source.
665
+ * @param exportConfig - The export configuration providing the date range and aggregation.
666
+ * @returns A promise that resolves to the readable data points and their fetched series data.
667
+ */
668
+ async fetchReadableSeriesOfSource(datapoints, exportConfig) {
669
+ const fetchGroup = async (groupToFetch) => {
670
+ try {
671
+ return await this.fetchSeriesData({
672
+ dateFrom: exportConfig.dateFrom,
673
+ dateTo: exportConfig.dateTo,
674
+ source: groupToFetch[0].source,
675
+ series: groupToFetch.map(datapoint => this.getSeriesName(datapoint)),
676
+ aggregationType: exportConfig.aggregation
677
+ });
678
+ }
679
+ catch (error) {
680
+ // A rejection (fetchSeriesData rethrows only 422) is terminal for this source — the 403
681
+ // fallback below does not apply — so surface the failure instead of propagating it.
682
+ this.alertService.addServerFailure(error);
683
+ return undefined;
684
+ }
685
+ };
686
+ const fetched = await fetchGroup(datapoints);
687
+ if (fetched?.res?.status === 200) {
688
+ return {
689
+ readableDatapoints: datapoints,
690
+ sourceItem: { source: datapoints[0].source, data: fetched.data }
595
691
  };
596
- const { data } = await this.fetchSeriesData(rawFilter);
597
- return { source, data };
598
- });
599
- const fetchedDataGroupedBySource = await Promise.all(fetchedDataPromises);
600
- return this.processSeriesData(exportConfig.datapointDetails, fetchedDataGroupedBySource);
692
+ }
693
+ const noneReadable = { readableDatapoints: [], sourceItem: null };
694
+ // Only an authorization failure justifies falling back to per-series probes.
695
+ if (fetched?.res?.status !== 403 || datapoints.length === 1) {
696
+ return noneReadable;
697
+ }
698
+ const readableDatapoints = await this.filterReadableDatapoints(datapoints);
699
+ if (readableDatapoints.length === 0) {
700
+ return noneReadable;
701
+ }
702
+ const refetched = await fetchGroup(readableDatapoints);
703
+ if (refetched?.res?.status !== 200) {
704
+ return noneReadable;
705
+ }
706
+ return {
707
+ readableDatapoints,
708
+ sourceItem: { source: readableDatapoints[0].source, data: refetched.data }
709
+ };
710
+ }
711
+ /**
712
+ * Probes each data point individually and returns those the user is permitted to read.
713
+ *
714
+ * @param datapoints - The data points to probe.
715
+ * @returns A promise that resolves to the readable data points.
716
+ */
717
+ async filterReadableDatapoints(datapoints) {
718
+ const probedDatapoints = await Promise.all(datapoints.map(async (datapoint) => (await this.isSeriesReadable(datapoint.source, [this.getSeriesName(datapoint)]))
719
+ ? datapoint
720
+ : null));
721
+ return probedDatapoints.filter(datapoint => datapoint !== null);
722
+ }
723
+ /**
724
+ * Returns the series name (`valueFragmentType.valueFragmentSeries`) of a data point.
725
+ */
726
+ getSeriesName({ valueFragmentType, valueFragmentSeries }) {
727
+ return `${valueFragmentType}.${valueFragmentSeries}`;
728
+ }
729
+ /**
730
+ * Probes whether the user is permitted to read the given series of a source.
731
+ * Only the response status matters, so the date range is intentionally empty (now..now)
732
+ * to keep the probe free of measurement values — it is not a data fetch.
733
+ *
734
+ * @param source - The ID of the managed object the series belong to.
735
+ * @param series - The series names (`valueFragmentType.valueFragmentSeries`) to probe.
736
+ * @returns A promise that resolves to true when the probe request succeeds.
737
+ */
738
+ async isSeriesReadable(source, series) {
739
+ const dateFrom = new Date();
740
+ const rawFilter = {
741
+ dateFrom,
742
+ dateTo: dateFrom,
743
+ source,
744
+ series
745
+ };
746
+ try {
747
+ const { res } = await this.fetchSeriesData(rawFilter);
748
+ return res?.status === 200;
749
+ }
750
+ catch {
751
+ // A failed probe means the series cannot be read; must not reject the whole permission check.
752
+ return false;
753
+ }
601
754
  }
602
755
  prepareSeriesFilter(filters, roundSeconds) {
603
756
  const { dateFrom, dateTo, source, series, aggregationType } = filters;
@@ -1421,14 +1574,24 @@ class DatapointsExportSelectorFileExporterComponent {
1421
1574
  this.isListTypeOfExport = this.exportConfig().exportType === 'latestWithDetails';
1422
1575
  this.isDataScopeVisible = !this.isListTypeOfExport;
1423
1576
  this.emitPreviewAvailability();
1424
- const sourcesPermittedToRead = await this.dataFetchingService.getSourcesWithPermissionsToRead(this.exportConfig().datapointDetails);
1425
- if (sourcesPermittedToRead.length === 0) {
1577
+ let permittedDatapoints;
1578
+ let initialSeriesData = [];
1579
+ if (this.isListTypeOfExport) {
1580
+ permittedDatapoints = await this.dataFetchingService.getDatapointsWithPermissionsToRead(this.exportConfig().datapointDetails);
1581
+ }
1582
+ else {
1583
+ // The initial data fetch doubles as the permission check to avoid separate probe requests.
1584
+ const { dataToExport, readableDatapoints } = await this.dataFetchingService.fetchAndPrepareSeriesDataWithPermissions(this.exportConfig());
1585
+ permittedDatapoints = readableDatapoints;
1586
+ initialSeriesData = dataToExport;
1587
+ }
1588
+ if (permittedDatapoints.length === 0) {
1426
1589
  this.isCheckingPermissions = false;
1427
1590
  this.cdr.markForCheck();
1428
1591
  return;
1429
1592
  }
1430
- if (sourcesPermittedToRead.length !== this.exportConfig().datapointDetails.length) {
1431
- this.exportConfig().datapointDetails = this.exportConfig().datapointDetails.filter(dataPoint => sourcesPermittedToRead.includes(String(dataPoint.source)));
1593
+ if (permittedDatapoints.length !== this.exportConfig().datapointDetails.length) {
1594
+ this.exportConfig().datapointDetails = permittedDatapoints;
1432
1595
  }
1433
1596
  const hasFullExportPermission = await this.dataFetchingService.hasPermissionToUseMeasurementsApi(this.exportConfig());
1434
1597
  if (!hasFullExportPermission) {
@@ -1445,7 +1608,9 @@ class DatapointsExportSelectorFileExporterComponent {
1445
1608
  await this.loadListExportData();
1446
1609
  }
1447
1610
  else {
1448
- await this.loadTableExportData();
1611
+ // Reuse the data fetched by the permission check instead of fetching it again.
1612
+ this.dataToExport = initialSeriesData;
1613
+ this.determineShowingPreviewOrEmptyState();
1449
1614
  this.handleExportModeChanges();
1450
1615
  }
1451
1616
  this.handleDateSelectorChanges();
@@ -2380,11 +2545,11 @@ class DatapointsExportSelectorComponent {
2380
2545
  this.isOpen.emit(await modalRef.result);
2381
2546
  }
2382
2547
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: DatapointsExportSelectorComponent, deps: [{ token: i1$1.BsModalService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
2383
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: DatapointsExportSelectorComponent, isStandalone: true, selector: "c8y-datapoints-export-selector", inputs: { displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, containerClass: { classPropertyName: "containerClass", publicName: "containerClass", isSignal: true, isRequired: false, transformFunction: null }, exportConfig: { classPropertyName: "exportConfig", publicName: "exportConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpen" }, ngImport: i0, template: "@switch (displayMode()) {\n @case ('default') {\n @if (containerClass() !== 'd-contents') {\n <div [class]=\"containerClass() || DEFAULT_CSS_STYLE\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"isExportDisabled()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n } @else {\n <button\n class=\"btn btn-link\"\n type=\"button\"\n (click)=\"openExportModal()\"\n [disabled]=\"isExportDisabled()\"\n >\n <i c8yIcon=\"data-export\"></i>\n {{ 'Generate export' | translate }}\n </button>\n }\n }\n @case ('icon-only') {\n <button\n class=\"btn btn-icon\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2548
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: DatapointsExportSelectorComponent, isStandalone: true, selector: "c8y-datapoints-export-selector", inputs: { displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, containerClass: { classPropertyName: "containerClass", publicName: "containerClass", isSignal: true, isRequired: false, transformFunction: null }, exportConfig: { classPropertyName: "exportConfig", publicName: "exportConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpen" }, ngImport: i0, template: "@switch (displayMode()) {\n @case ('default') {\n @if (containerClass() !== 'd-contents') {\n <div [class]=\"containerClass() || DEFAULT_CSS_STYLE\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"isExportDisabled()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n } @else {\n <button\n class=\"btn btn-link\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [disabled]=\"isExportDisabled()\"\n >\n <i c8yIcon=\"data-export\"></i>\n {{ 'Generate export' | translate }}\n </button>\n }\n }\n @case ('icon-only') {\n <button\n class=\"btn btn-icon\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2384
2549
  }
2385
2550
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: DatapointsExportSelectorComponent, decorators: [{
2386
2551
  type: Component,
2387
- args: [{ selector: 'c8y-datapoints-export-selector', imports: [CommonModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@switch (displayMode()) {\n @case ('default') {\n @if (containerClass() !== 'd-contents') {\n <div [class]=\"containerClass() || DEFAULT_CSS_STYLE\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"isExportDisabled()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n } @else {\n <button\n class=\"btn btn-link\"\n type=\"button\"\n (click)=\"openExportModal()\"\n [disabled]=\"isExportDisabled()\"\n >\n <i c8yIcon=\"data-export\"></i>\n {{ 'Generate export' | translate }}\n </button>\n }\n }\n @case ('icon-only') {\n <button\n class=\"btn btn-icon\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n }\n}\n" }]
2552
+ args: [{ selector: 'c8y-datapoints-export-selector', imports: [CommonModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@switch (displayMode()) {\n @case ('default') {\n @if (containerClass() !== 'd-contents') {\n <div [class]=\"containerClass() || DEFAULT_CSS_STYLE\">\n <button\n class=\"btn btn-default btn-sm\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"isExportDisabled()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n } @else {\n <button\n class=\"btn btn-link\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [disabled]=\"isExportDisabled()\"\n >\n <i c8yIcon=\"data-export\"></i>\n {{ 'Generate export' | translate }}\n </button>\n }\n }\n @case ('icon-only') {\n <button\n class=\"btn btn-icon\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n data-cy=\"datapoints-export-selector--open-export-button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig()\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n }\n}\n" }]
2388
2553
  }], ctorParameters: () => [{ type: i1$1.BsModalService }, { type: i1.GainsightService }], propDecorators: { displayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayMode", required: false }] }], containerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerClass", required: false }] }], exportConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportConfig", required: false }] }], isOpen: [{ type: i0.Output, args: ["isOpen"] }] } });
2389
2554
 
2390
2555
  /**