Skip to content

Commit 15e6107

Browse files
author
Amineta Lo
committed
feat(pfCard): Add a loading state to the pfCards
1 parent 26acd65 commit 15e6107

File tree

8 files changed

+225
-26
lines changed

8 files changed

+225
-26
lines changed

src/card/aggregate-status/aggregate-status-card.component.js

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
* </ul>
3131
* </ul>
3232
* @param {boolean=} show-top-border Show/hide the top border, true shows top border, false (default) hides top border
33+
* @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner
34+
* @param {string=} spinnerText Text for the card spinner
35+
* @param {string=} loadingCardHeight Height to set for the card when data is loading
3336
* @param {string=} layout Various alternative layouts the aggregate status card may have:<br/>
3437
* <ul style='list-style-type: none'>
3538
* <li>'mini' displays a mini aggregate status card. Note: when using 'mini' layout, only one notification can be specified in the status object
@@ -63,13 +66,19 @@
6366
<i>(depreciated, use layout = 'tall' instead)</i>
6467
</br></br>
6568
<pf-aggregate-status-card status="aggStatusAlt" show-top-border="true" alt-layout="true"></pf-aggregate-status-card>
69+
<br/>
70+
<label>Loading State</label>
71+
<pf-aggregate-status-card status="aggStatusAlt2" loading-card-height="150px" show-top-border="true" show-spinner="dataLoading" spinner-text="Loading" layout="tall"></pf-aggregate-status-card>
6672
</div>
6773
</div>
6874
</file>
6975
7076
<file name="script.js">
71-
angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window ) {
77+
angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window, $timeout ) {
7278
var imagePath = $window.IMAGE_PATH || "img";
79+
80+
$scope.dataLoading = true;
81+
7382
$scope.status = {
7483
"title":"Nodes",
7584
"count":793,
@@ -103,28 +112,49 @@
103112
"href":"#"
104113
}
105114
]
106-
};
115+
};
116+
117+
$timeout(function () {
118+
$scope.dataLoading = false;
119+
120+
$scope.aggStatusAlt2 = {
121+
"title":"Providers",
122+
"count":3,
123+
"notifications":[
124+
{
125+
"iconImage": imagePath + "/kubernetes.svg",
126+
"count":1,
127+
"href":"#"
128+
},
129+
{
130+
"iconImage": imagePath + "/OpenShift-logo.svg",
131+
"count":2,
132+
"href":"#"
133+
}
134+
]
135+
};
136+
}, 3000 );
107137
108-
$scope.miniAggStatus = {
138+
$scope.miniAggStatus = {
109139
"iconClass":"pficon pficon-container-node",
110140
"title":"Nodes",
111141
"count":52,
112142
"href":"#",
113143
"notification": {
114-
"iconClass":"pficon pficon-error-circle-o",
115-
"count":3
116-
}
117-
};
144+
"iconClass":"pficon pficon-error-circle-o",
145+
"count":3
146+
}
147+
};
118148
119-
$scope.miniAggStatus2 = {
149+
$scope.miniAggStatus2 = {
120150
"iconClass":"pficon pficon-cluster",
121151
"title":"Adipiscing",
122152
"count":9,
123153
"href":"#",
124154
"notification":{
125-
"iconClass":"pficon pficon-ok"
126-
}
127-
};
155+
"iconClass":"pficon pficon-ok"
156+
}
157+
};
128158
});
129159
</file>
130160
@@ -135,6 +165,9 @@ angular.module( 'patternfly.card' ).component('pfAggregateStatusCard', {
135165
bindings: {
136166
status: '=',
137167
showTopBorder: '@?',
168+
showSpinner: '<?',
169+
spinnerText: '@?',
170+
loadingCardHeight: '@?',
138171
altLayout: '@?',
139172
layout: '@?'
140173
},
@@ -146,6 +179,11 @@ angular.module( 'patternfly.card' ).component('pfAggregateStatusCard', {
146179
ctrl.shouldShowTopBorder = (ctrl.showTopBorder === 'true');
147180
ctrl.isAltLayout = (ctrl.altLayout === 'true' || ctrl.layout === 'tall');
148181
ctrl.isMiniLayout = (ctrl.layout === 'mini');
182+
ctrl.showSpinner = ctrl.showSpinner === true;
183+
184+
if (ctrl.loadingCardHeight) {
185+
ctrl.spinnerHeight = {'height': ctrl.loadingCardHeight};
186+
}
149187
};
150188
}
151189
});

src/card/aggregate-status/aggregate-status-card.html

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<div ng-if="!$ctrl.isMiniLayout" class="card-pf card-pf-aggregate-status" ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder, 'card-pf-aggregate-status-alt': $ctrl.isAltLayout}">
2-
<h2 class="card-pf-title">
1+
<div ng-if="!$ctrl.isMiniLayout" class="card-pf card-pf-aggregate-status" ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder, 'card-pf-aggregate-status-alt': $ctrl.isAltLayout}" ng-style="$ctrl.spinnerHeight">
2+
<h2 class="card-pf-title" ng-class="{'hide-for-spinner': $ctrl.showSpinner}">
33
<a href="{{$ctrl.status.href}}" ng-if="$ctrl.status.href">
44
<image ng-if="$ctrl.status.iconImage" ng-src="{{$ctrl.status.iconImage}}" alt="" class="card-pf-icon-image"></image>
55
<span class="{{$ctrl.status.iconClass}}"></span>
@@ -13,8 +13,15 @@ <h2 class="card-pf-title">
1313
<span class="card-pf-aggregate-status-title">{{$ctrl.status.title}}</span>
1414
</span>
1515
</h2>
16-
<div class="card-pf-body">
17-
<p class="card-pf-aggregate-status-notifications">
16+
<div class="card-pf-body" ng-class="{'show-spinner': $ctrl.showSpinner}" ng-style="$ctrl.spinnerHeight">
17+
<div ng-if="$ctrl.showSpinner" class="spinner-container">
18+
<div class="loading-indicator">
19+
<span class="spinner spinner-lg" aria-hidden="true"></span>
20+
<span ng-if="$ctrl.spinnerText" class="loading-text">{{$ctrl.spinnerText}}</span>
21+
<label ng-if="!$ctrl.spinnerText" class="sr-only">Loading</label>
22+
</div>
23+
</div>
24+
<p class="card-pf-aggregate-status-notifications" ng-class="{'hide-for-spinner': $ctrl.showSpinner}">
1825
<span class="card-pf-aggregate-status-notification" ng-repeat="notification in $ctrl.status.notifications">
1926
<a href="{{notification.href}}" ng-if="notification.href">
2027
<image ng-if="notification.iconImage" ng-src="{{notification.iconImage}}" alt="" class="card-pf-icon-image"></image>
@@ -28,8 +35,8 @@ <h2 class="card-pf-title">
2835
</p>
2936
</div>
3037
</div>
31-
<div ng-if="$ctrl.isMiniLayout" class="card-pf card-pf-aggregate-status card-pf-aggregate-status-mini" ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder}">
32-
<h2 class="card-pf-title">
38+
<div ng-if="$ctrl.isMiniLayout" class="card-pf card-pf-aggregate-status card-pf-aggregate-status-mini" ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder}" ng-style="$ctrl.spinnerHeight">
39+
<h2 class="card-pf-title" ng-class="{'hide-for-spinner': $ctrl.showSpinner}">
3340
<a ng-if="$ctrl.status.href" href="{{$ctrl.status.href}}">
3441
<image ng-if="$ctrl.status.iconImage" ng-src="{{$ctrl.status.iconImage}}" alt="" class="card-pf-icon-image"></image>
3542
<span ng-if="$ctrl.status.iconClass" class="{{$ctrl.status.iconClass}}"></span>
@@ -41,7 +48,14 @@ <h2 class="card-pf-title">
4148
{{$ctrl.status.title}}
4249
</span>
4350
</h2>
44-
<div class="card-pf-body">
51+
<div class="card-pf-body" ng-class="{'show-spinner': $ctrl.showSpinner}">
52+
<div ng-if="$ctrl.showSpinner" class="spinner-container">
53+
<div class="loading-indicator">
54+
<span class="spinner spinner-lg" aria-hidden="true"></span>
55+
<span ng-if="$ctrl.spinnerText" class="loading-text">{{$ctrl.spinnerText}}</span>
56+
<label ng-if="!$ctrl.spinnerText" class="sr-only">Loading</label>
57+
</div>
58+
</div>
4559
<p ng-if="$ctrl.status.notification.iconImage || $ctrl.status.notification.iconClass || $ctrl.status.notification.count" class="card-pf-aggregate-status-notifications">
4660
<span class="card-pf-aggregate-status-notification">
4761
<a ng-if="$ctrl.status.notification.href" href="{{$ctrl.status.notification.href}}">

src/card/card.less

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@
6464
}
6565
}
6666

67+
.card-pf-aggregate-status-mini {
68+
.card-pf-body {
69+
&.show-spinner {
70+
height: 40px;
71+
}
72+
.spinner-container {
73+
top: -22px;
74+
}
75+
}
76+
}
77+
6778
.card-pf-heading-no-bottom {
6879
margin: 0 -20px 0px;
6980
padding: 0 20px 0;
@@ -133,4 +144,7 @@
133144
margin-bottom: 15px;
134145
}
135146
}
147+
.show-spinner {
148+
width: 100%;
149+
}
136150
}

src/card/examples/card-trend.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
*
66
* @param {string} headTitle Title for the card
77
* @param {string=} subTitle Sub-Title for the card
8+
* @param {string=} spinnerText Text for the card spinner
89
* @param {boolean=} showTopBorder Show/Hide the blue top border. True shows top border, false (default) hides top border
10+
* @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner
911
* @param {boolean=} showTitlesSeparator Show/Hide the grey line between the title and sub-title.
1012
* True (default) shows the line, false hides the line
1113
* @param {object=} footer footer configuration properties:<br/>
@@ -38,7 +40,7 @@
3840
<pf-card head-title="Cluster Utilization" show-top-border="true" footer="footerConfig" filter="filterConfig" style="width: 50%">
3941
<pf-trends-chart config="configSingle" chart-data="dataSingle"></pf-trends-chart>
4042
</pf-card>
41-
<pf-card head-title="Cluster Utilization" show-top-border="true" footer="footerConfig" filter="filterConfig" style="width: 50%">
43+
<pf-card head-title="Cluster Utilization" show-top-border="true" show-spinner="dataLoading" spinner-text="Loading" footer="footerConfig" filter="filterConfig" style="width: 50%">
4244
<pf-trends-chart config="configRightLabel" chart-data="dataSingle"></pf-trends-chart>
4345
</pf-card>
4446
<label class="label-title">Card with Multiple Trends</label>
@@ -52,7 +54,13 @@
5254
</div>
5355
</file>
5456
<file name="script.js">
55-
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope ) {
57+
angular.module( 'demo', ['patternfly.charts', 'patternfly.card'] ).controller( 'ChartCtrl', function( $scope, $timeout ) {
58+
59+
$scope.dataLoading = true;
60+
61+
$timeout(function () {
62+
$scope.dataLoading = false;
63+
}, 3000 );
5664
5765
$scope.footerConfig = {
5866
'iconClass' : 'fa fa-flag',

src/card/info-status/info-status-card.component.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
* </ul>
1414
* @param {boolean=} show-top-border Show/hide the top border, true shows top border, false (default) hides top border
1515
* @param {boolean} htmlContent Flag to allow HTML content within the info options
16+
* @param {boolean=} showSpinner Show/Hide the spinner for loading state. True shows the spinner, false (default) hides the spinner
17+
* @param {string=} spinnerText Text for the card spinner
18+
* @param {string=} loadingCardHeight Height to set for the card when data is loading
1619
*
1720
* @description
1821
* Component for easily displaying textual information
@@ -31,13 +34,19 @@
3134
<br/>
3235
<label>With HTML</label>
3336
<pf-info-status-card status="infoStatusAlt" html-content="true"></pf-info-status-card>
37+
<br/>
38+
<label>Loading State</label>
39+
<pf-info-status-card status="infoStatus2" loading-card-height="200px" show-top-border="true" show-spinner="dataLoading" spinner-text="Loading"></pf-info-status-card>
3440
</div>
3541
</div>
3642
</file>
3743
3844
<file name="script.js">
39-
angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window ) {
45+
angular.module( 'patternfly.card' ).controller( 'CardDemoCtrl', function( $scope, $window, $timeout ) {
4046
var imagePath = $window.IMAGE_PATH || "img";
47+
48+
$scope.dataLoading = true;
49+
4150
$scope.infoStatus = {
4251
"title":"TinyCore-local",
4352
"href":"#",
@@ -50,6 +59,22 @@
5059
]
5160
};
5261
62+
$timeout(function () {
63+
$scope.dataLoading = false;
64+
65+
$scope.infoStatus2 = {
66+
"title":"TinyCore-local",
67+
"href":"#",
68+
"iconClass": "fa fa-shield",
69+
"info":[
70+
"VM Name: aapdemo002",
71+
"Host Name: localhost.localdomian",
72+
"IP Address: 10.9.62.100",
73+
"Power status: on"
74+
]
75+
};
76+
}, 3000 );
77+
5378
$scope.infoStatusTitless = {
5479
"iconImage": imagePath + "/OpenShift-logo.svg",
5580
"info":[
@@ -79,6 +104,9 @@ angular.module( 'patternfly.card' ).component('pfInfoStatusCard', {
79104
bindings: {
80105
status: '=',
81106
showTopBorder: '@?',
107+
showSpinner: '<?',
108+
spinnerText: '@?',
109+
loadingCardHeight: '@?',
82110
htmlContent: '@?'
83111
},
84112
templateUrl: 'card/info-status/info-status-card.html',
@@ -88,9 +116,14 @@ angular.module( 'patternfly.card' ).component('pfInfoStatusCard', {
88116
ctrl.$onInit = function () {
89117
ctrl.shouldShowTopBorder = (ctrl.showTopBorder === 'true');
90118
ctrl.shouldShowHtmlContent = (ctrl.htmlContent === 'true');
119+
ctrl.showSpinner = ctrl.showSpinner === true;
91120
ctrl.trustAsHtml = function (html) {
92121
return $sce.trustAsHtml(html);
93122
};
123+
124+
if (ctrl.loadingCardHeight) {
125+
ctrl.spinnerHeight = {'height': ctrl.loadingCardHeight};
126+
}
94127
};
95128
}
96129
});

src/card/info-status/info-status-card.html

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
<div class="card-pf card-pf-info-status"
2-
ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder}">
3-
<div class="card-pf-info-image">
1+
<div class="card-pf card-pf-info-status" ng-class="{'card-pf-accented': $ctrl.shouldShowTopBorder}" ng-style="$ctrl.spinnerHeight">
2+
<div ng-if="$ctrl.showSpinner" class="card-pf-body show-spinner" ng-style="$ctrl.spinnerHeight">
3+
<div class="spinner-container">
4+
<div class="loading-indicator">
5+
<span class="spinner spinner-lg" aria-hidden="true"></span>
6+
<span ng-if="$ctrl.spinnerText" class="loading-text">{{$ctrl.spinnerText}}</span>
7+
<label ng-if="!$ctrl.spinnerText" class="sr-only">Loading</label>
8+
</div>
9+
</div>
10+
</div>
11+
<div ng-if="!$ctrl.showSpinner" class="card-pf-info-image">
412
<img ng-if="$ctrl.status.iconImage" ng-src="{{$ctrl.status.iconImage}}" alt=""
513
class="info-img"/>
614
<span class="info-icon {{$ctrl.status.iconClass}}"></span>
715
</div>
8-
<div class="card-pf-info-content">
16+
<div ng-if="!$ctrl.showSpinner" class="card-pf-info-content card-pf-body" ng-class="{'show-spinner': $ctrl.showSpinner}">
917
<h2 class="card-pf-title" ng-if="$ctrl.status.title">
1018
<a href="{{$ctrl.status.href}}" ng-if="$ctrl.status.href">
1119
<span class="">{{$ctrl.status.title}}</span>

test/card/aggregate-status/aggregate-status-card.component.spec.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('Component: pfAggregateStatusCard', function() {
2222
"title":"Nodes",
2323
"count":793,
2424
"href":"#",
25-
"iconClass": "fa fa-shield",
25+
"iconClass": "fa fa-shield"
2626
};
2727

2828
element = compileCard('<pf-aggregate-status-card status="status"></pf-aggregate-status-card>', $scope);
@@ -113,6 +113,48 @@ describe('Component: pfAggregateStatusCard', function() {
113113
expect(cardClass).toBeFalsy();
114114
});
115115

116+
it("should show and hide the spinner", function() {
117+
118+
// When data is loaded, spinner should be hidden
119+
$scope.dataLoading = false;
120+
element = compileCard('<pf-aggregate-status-card status="aggStatusAlt2" show-spinner="dataLoading"></pf-aggregate-status-card>', $scope);
121+
cardClass = angular.element(element).find('.spinner-lg');
122+
expect(cardClass.length).toBe(0);
123+
124+
// When data is loading, spinner should be present
125+
$scope.dataLoading = true;
126+
$scope.$digest();
127+
cardClass = angular.element(element).find('.spinner-lg');
128+
expect(cardClass.length).toBe(1);
129+
});
130+
131+
it("should show and hide the spinner text", function() {
132+
133+
// When no spinner text is given, it should be undefined
134+
element = compileCard('<pf-aggregate-status-card status="aggStatusAlt2" show-spinner="dataLoading"></pf-aggregate-status-card>', $scope);
135+
cardClass = angular.element(element).find('.loading-text');
136+
expect(cardClass.html()).toBeUndefined();
137+
138+
// When data is loading, spinner text should be present
139+
$scope.dataLoading = true;
140+
element = compileCard('<pf-aggregate-status-card status="aggStatusAlt2" show-spinner="dataLoading" spinner-text="Test Loading Message"></pf-aggregate-status-card>', $scope);
141+
cardClass = angular.element(element).find('.loading-text');
142+
expect(cardClass.html()).toContain('Test Loading Message');
143+
});
144+
145+
it("should have a loading card height when specified", function() {
146+
147+
// When a height is specified it should be present
148+
element = compileCard('<pf-aggregate-status-card status="aggStatusAlt2" loading-card-height="150px" show-spinner="dataLoading" spinner-text="Test Loading Message"></pf-aggregate-status-card>', $scope);
149+
cardClass = angular.element(element).find('.card-pf');
150+
expect(cardClass.css('height')).toBe('150px');
151+
152+
// When a height is not specified there should be none
153+
element = compileCard('<pf-aggregate-status-card status="aggStatusAlt2" show-spinner="dataLoading" spinner-text="Test Loading Message"></pf-aggregate-status-card>', $scope);
154+
cardClass = angular.element(element).find('.card-pf');
155+
expect(cardClass.css('height')).toBe('0px');
156+
});
157+
116158
it("should show mini layout", function() {
117159

118160
$scope.status = {

0 commit comments

Comments
 (0)