Skip to content

feat(pfCard): Add loading state for pfCard #661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 33 additions & 23 deletions src/card/basic/card.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*
* @param {string} headTitle Title for the card
* @param {string=} subTitle Sub-Title for the card
* @param {string=} spinnerText Text for the card spinner
* @param {boolean=} showTopBorder Show/Hide the blue top border. True shows top border, false (default) hides top border
* @param {boolean=} showTitlesSeparator Show/Hide the grey line between the title and sub-title.
* True (default) shows the line, false hides the line
* @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner
* @param {object=} footer footer configuration properties:<br/>
* <ul style='list-style-type: none'>
* <li>.iconClass - (optional) the icon to show on the bottom left of the footer panel
Expand Down Expand Up @@ -35,7 +37,7 @@
<file name="index.html">
<div ng-controller="ChartCtrl">
<label class="label-title">Card With Multiple Utilization Bars</label>
<pf-card head-title="System Resources" show-top-border="true" style="width: 65%">
<pf-card head-title="System Resources" show-spinner="dataLoading" spinner-text="Loading" show-top-border="true">
<pf-utilization-bar-chart chart-data=data2 chart-title=title2 layout=layoutInline units=units2 threshold-error="85" threshold-warning="60"></pf-utilization-bar-chart>
<pf-utilization-bar-chart chart-data=data3 chart-title=title3 layout=layoutInline units=units3 threshold-error="85" threshold-warning="60"></pf-utilization-bar-chart>
<pf-utilization-bar-chart chart-data=data4 chart-title=title4 layout=layoutInline units=units4 threshold-error="85" threshold-warning="60"></pf-utilization-bar-chart>
Expand All @@ -44,37 +46,44 @@
</div>
</file>
<file name="script.js">
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope ) {
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) {

$scope.dataLoading = true;

$scope.title2 = 'Memory';
$scope.units2 = 'GB';

$scope.data2 = {
'used': '25',
'total': '100'
};

$scope.title3 = 'CPU Usage';
$scope.units3 = 'MHz';

$scope.data3 = {
'used': '420',
'total': '500'
};

$scope.title4 = 'Disk Usage';
$scope.units4 = 'TB';
$scope.data4 = {
'used': '350',
'total': '500'
};

$scope.title5 = 'Disk I/O';
$scope.units5 = 'I/Ops';
$scope.data5 = {
'used': '450',
'total': '500'
};
$scope.units5 = 'I/Ops';

$timeout(function () {
$scope.dataLoading = false;

$scope.data2 = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure you need the $scope.dataN objects in the $timeout.

Copy link
Contributor Author

@amarie401 amarie401 Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtaylor113 I think they were already there in the file but were moved into the $timeout @jeff-phillips-18 would it be fine to remove the data objects? Is there a particular reason they were moved into the $timeout?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, trying to mimic what an application would do. If the data is loading, then there is no data until it is loaded. This uncovered an issue were we expect the data to always be set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks

'used': '25',
'total': '100'
};

$scope.data3 = {
'used': '420',
'total': '500'
};

$scope.data4 = {
'used': '350',
'total': '500'
};
$scope.data5 = {
'used': '450',
'total': '500'
};
}, 3000 );

$scope.layoutInline = {
'type': 'inline'
Expand All @@ -91,6 +100,8 @@ angular.module('patternfly.card').component('pfCard', {
subTitle: '@?',
showTopBorder: '@?',
showTitlesSeparator: '@?',
showSpinner: '<?',
spinnerText: '@?',
footer: '=?',
filter: '=?'
},
Expand All @@ -104,11 +115,9 @@ angular.module('patternfly.card').component('pfCard', {
ctrl.currentFilter = ctrl.filter.filters[0];
}
}

ctrl.footerCallBackFn = function () {
ctrl.footerCallBackResult = ctrl.footer.callBackFn();
};

ctrl.filterCallBackFn = function (f) {
ctrl.currentFilter = f;
if (ctrl.filter.callBackFn) {
Expand All @@ -130,6 +139,7 @@ angular.module('patternfly.card').component('pfCard', {

ctrl.$onInit = function () {
ctrl.shouldShowTitlesSeparator = (!ctrl.showTitlesSeparator || ctrl.showTitlesSeparator === 'true');
ctrl.showSpinner = ctrl.showSpinner === true;
};
}
});
18 changes: 12 additions & 6 deletions src/card/basic/card.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
<div ng-class="$ctrl.showTopBorder === 'true' ? 'card-pf card-pf-accented' : 'card-pf'">
<div class="card-pf" ng-class="{'card-pf-accented': $ctrl.showTopBorder === 'true'}">
<div ng-if="$ctrl.showHeader()" ng-class="$ctrl.shouldShowTitlesSeparator ? 'card-pf-heading' : 'card-pf-heading-no-bottom'">
<div ng-if="$ctrl.showFilterInHeader()" ng-include="'card/basic/card-filter.html'"></div>
<div ng-if="$ctrl.showFilterInHeader()" class="card-pf-footer-in-header" ng-class="{'hide-for-spinner': $ctrl.showSpinner}" ng-include="'card/basic/card-filter.html'"></div>
<h2 class="card-pf-title">{{$ctrl.headTitle}}</h2>
</div>

<span ng-if="$ctrl.subTitle" class="card-pf-subtitle">{{$ctrl.subTitle}}</span>

<div class="card-pf-body">
<div ng-transclude></div>
<div class="card-pf-body" ng-class="{'show-spinner': $ctrl.showSpinner}">
<div ng-class="{'hide-for-spinner': $ctrl.showSpinner}" ng-transclude></div>
<div ng-if="$ctrl.showSpinner" class="spinner-container">
<div class="loading-indicator">
<span class="spinner spinner-lg" aria-hidden="true"></span>
<span ng-if="$ctrl.spinnerText" class="loading-text">{{$ctrl.spinnerText}}</span>
<label ng-if="!$ctrl.spinnerText" class="sr-only">Loading</label>
</div>
</div>
</div>
<div ng-if="$ctrl.footer" class="card-pf-footer">
<div ng-if="$ctrl.footer" class="card-pf-footer" ng-class="{'hide-for-spinner': $ctrl.showSpinner}">
<div ng-if="$ctrl.showFilterInFooter()" ng-include="'card/basic/card-filter.html'"></div>
<p>
<a ng-if="$ctrl.footer.href" href="{{$ctrl.footer.href}}" ng-class="{'card-pf-link-with-icon':$ctrl.footer.iconClass,'card-pf-link':!$ctrl.footer.iconClass}">
Expand Down
35 changes: 35 additions & 0 deletions src/card/card.less
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,41 @@
}
}

.card-pf {
.hide-for-spinner {
opacity: 0;
}
.card-pf-body {
&.show-spinner {
position: relative;
}
.spinner-container {
display: flex;
justify-content: center;
left: 0;
position: absolute;
right: 0;
top: -20px;
bottom: 0;

.loading-indicator {
align-items: center;
justify-content: center;
display: flex;

.loading-text {
font-size: 16px;
margin-left: 10px;
}

.spinner.spinner-lg {
display: inline-block;
}
}
}
}
}

.card-pf-heading-no-bottom {
margin: 0 -20px 0px;
padding: 0 20px 0;
Expand Down
14 changes: 13 additions & 1 deletion src/card/examples/card-timeframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*
* @param {string} headTitle Title for the card
* @param {string=} subTitle Sub-Title for the card
* @param {string=} spinnerText Text for the card spinner
* @param {boolean=} showTopBorder Show/Hide the blue top border. True shows top border, false (default) hides top border
* @param {boolean=} showTitlesSeparator Show/Hide the grey line between the title and sub-title.
* True (default) shows the line, false hides the line
* @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner
* @param {object=} footer footer configuration properties:<br/>
* <ul style='list-style-type: none'>
* <li>.iconClass - (optional) the icon to show on the bottom left of the footer panel
Expand Down Expand Up @@ -45,10 +47,20 @@
footer="footerConfig" filter="filterConfig" style="width: 50%">
Card Contents
</pf-card>
<label class="label-title">Loading State</label>
<pf-card show-spinner="dataLoading" spinner-text="Loading" head-title="Card Title" sub-title="Card Subtitle" show-top-border="true" filter="filterConfigHeader" style="width: 50%">
Card Contents
</pf-card>
</div>
</file>
<file name="script.js">
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope ) {
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) {

$scope.dataLoading = true;

$timeout(function () {
$scope.dataLoading = false;
}, 3000 );

$scope.footerConfig = {
'iconClass' : 'fa fa-flag',
Expand Down
14 changes: 11 additions & 3 deletions src/charts/utilization-bar/utilization-bar-chart.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,16 @@ angular.module('patternfly.charts').component('pfUtilizationBarChart', {
prevChartData = angular.copy(ctrl.chartData);
prevLayout = angular.copy(ctrl.layout);

if (!ctrl.chartData) {
return;
}

//Calculate the percentage used
ctrl.chartData.percentageUsed = Math.round(100 * (ctrl.chartData.used / ctrl.chartData.total));
if (!isNaN(ctrl.chartData.used) && !isNaN(ctrl.chartData.total) && (ctrl.chartData.total > 0)) {
ctrl.chartData.percentageUsed = Math.round(100 * (ctrl.chartData.used / ctrl.chartData.total));
} else {
ctrl.chartData.percentageUsed = 0;
}

if (ctrl.thresholdError || ctrl.thresholdWarning) {
ctrl.isError = (ctrl.chartData.percentageUsed >= ctrl.thresholdError);
Expand Down Expand Up @@ -201,11 +209,11 @@ angular.module('patternfly.charts').component('pfUtilizationBarChart', {
};

ctrl.usedTooltipMessage = function () {
return ctrl.usedTooltipFunction ? ctrl.usedTooltipFunction() : ctrl.chartData.percentageUsed + '% Used';
return ctrl.usedTooltipFunction ? ctrl.usedTooltipFunction() : _.get(ctrl.chartData, 'percentageUsed', 'N/A') + '% Used';
};

ctrl.availableTooltipMessage = function () {
return ctrl.availableTooltipFunction ? ctrl.availableTooltipFunction() : (100 - ctrl.chartData.percentageUsed) + '% Available';
return ctrl.availableTooltipFunction ? ctrl.availableTooltipFunction() : (100 - _.get(ctrl.chartData, 'percentageUsed', 0)) + '% Available';
};
}
});
30 changes: 30 additions & 0 deletions test/card/basic/card.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,36 @@ describe('Component: pfCard', function() {

});

it("should show and hide the spinner", function() {

// When data is loaded, spinner should be hidden
$scope.dataLoading = false;
element = compileCard('<pf-card head-title="My card title" show-spinner="dataLoading" sub-title="My card subtitle title" show-top-border="false">Inner content goes here</pf-card>', $scope);
cardClass = angular.element(element).find('.spinner-lg');
expect(cardClass.length).toBe(0);

// When data is loading, spinner should be present
$scope.dataLoading = true;
$scope.$digest();
cardClass = angular.element(element).find('.spinner-lg');
expect(cardClass.length).toBe(1);
});

it("should show and hide the spinner text", function() {

// When no spinner text is given, it should be undefined
element = compileCard('<pf-card head-title="My card title" show-spinner="dataLoading" sub-title="My card subtitle title" show-top-border="false">Inner content goes here</pf-card>', $scope);
cardClass = angular.element(element).find('.loading-text');
expect(cardClass.html()).toBeUndefined();

// When data is loading, spinner text should be present
$scope.dataLoading = true;
element = compileCard('<pf-card head-title="My card title" show-spinner="dataLoading" spinner-text="Test Loading Message" sub-title="My card subtitle title" show-top-border="false">Inner content goes here</pf-card>', $scope);
cardClass = angular.element(element).find('.loading-text');
expect(cardClass.html()).toContain('Test Loading Message');

});

it("should hide the action bar footer by default", function() {

// by default, if footer not defined, footer should not be shown
Expand Down
6 changes: 6 additions & 0 deletions test/charts/utilization-bar/utilization-bar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,10 @@ describe('Directive: pfUtilizationBarChart', function() {
expect(emptyChart.length).toBe(1);
});

it("should handle no data given", function() {
element = compileChart('<pf-utilization-bar-chart chart-data=data2 chart-title=title units=units></pf-utilization-bar-chart>', $scope);
utilizationBar = angular.element(element).find('.progress-bar-remaining').css('width');
expect(utilizationBar).toBe("100%");
});

});