The SKY UX template provides the skyuxconfig.json file to specify configuration settings. In addition, you can create additional configuration files with the skyuxconfig.[COMMAND].json naming convention to apply configuration settings specifically to SKY UX CLI commands.
+
Use these instructions to apply configuration settings to CLI commands.
+
+
+
+ Apply configuration settings to a SKY UX CLI command
+
+
+
In a local editor, create a configuration file in the root directory alongside the project's default skyuxconfig.json file. Follow the skyuxconfig.[COMMAND].json naming convention.
+
For example, to apply configuration settings when skyux e2e runs, create skyuxconfig.e2e.json.
+
In the new file, specify the configuration settings to apply when the command runs.
+
For example, you can specify a mock server URL to use when you run skyux e2e.
Save your changes. When you run SKY UX CLI commands, SKY UX automatically reads any files with the skyuxconfig.[COMMAND].json naming convention. For example, SKY UX includes and merges the settings in skyuxconfig.e2e.json when you run skyux e2e.
The appSettings property in skyuxconfig.json enables you to reuse data throughout a SPA. The appSettings property accepts strings, arrays, and any other type of configuration setting that is necessary. Use these instructions to access the appSettings data throughout your application.
+
+ If you create files to apply configuration settings to specific SKY UX CLI commands, you can provide command-specific appSettings data in those configuration files.
+
+
+ Reference appSettings in an index.html file
+
+
+
In a local editor, open the project's skyuxconfig.json file in the root directory.
+
Insert the appSettings property and specify the data to reuse throughout the application. For example:
In the template file for the component or service, pull in the config service and reference the appSettings value to access it in the .html file. For example:
When you create a SKY UX project, the SKY UX template provides a skyuxconfig.json file in your project's directory to enable you to configure settings for SKY UX. All properties in skyuxconfig.json are optional, and by default, the file only specifies values for the mode and compileMode settings.
-
-
The configuration options that are available in the skyuxconfig.json file include:
-
-
name — Specifies the name of the project when running in SKY UX Host. For example, if you specify "demo" as the name, then your SPA is accessible from https://host.nxt.blackbaud.com/demo. By default, SKY UX Builder uses the name property in the package.json file, minus the "blackbaud-skyux-spa-" prefix. That value is based on the root directory name that you specify when you create a SKY UX project. You can use the name property in skyuxconfig.json to overwrite the default name if you do not want the root directory name in your public-facing URL. For example, if you publish the SPA to NPM, you use the name property in package.json, so you may want to use a different name for the project in SKY UX Host.
-
mode — Controls how much boilerplate code to generate automatically. By default, the skyuxconfig.json file sets this property to easy. In easy mode, SKY UX automatically generates routes based the project's folder structure, provides bootstrapping to initialize the application, and supplies SKY UX components to the application. To override these options, change the mode property to advanced. Since advanced mode does not provide automatic route configuration, you can adjust URLs and point to specific components in your SPA. This property will eventually become obsolete as the SKY UX team builds out the CLI to allow users to override individual steps without switching to advanced mode.
-
compileMode — Specifies how to compile the project's code. By default, the skyuxconfig.json file sets this property to aot to specify ahead-of-time compilation, which runs the compiler once at build time using one set of libraries. You can change this setting to jit to specify just-in-time compilation, which runs the compiler at run time for every user using a different set of libraries. SKY UX uses ahead-of-time compilation by default because it is typically more performant and has less to handle at run-time. For more information about the difference, see the Angular documentation
-
host — Specifies configuration options for communication with SKY UX Host.
-
-
url — Specifies a base URL to pass information from skyux serve to the SKY UX Host. The default value is https://host.nxt.blackbaud.com.
-
-
-
app — Specifies configuration options for the local app when running skyux serve.
-
-
title — Controls the page title at the template level while waiting for the application to load. After it loads, you can use the Angular title service to set the page title. The default is Blackbaud - SKY UX Application, so if you do not want this default to appear in the title bar while the application loads, use the title to provide your preferred title.
-
port — Configures a port for the skyux serve command to use. If no port is specified, skyux serve dynamically finds an available port.
-
externals — Dynamically injects CSS and JavaScript files into SKY UX Host. You should have a specific use-case for an external. For example, Office Addins require their library to load through CDN in the head. The before and after sections of an external indicate whether to include the external resource before or after the default SKY UX Builder resources. The head property, which only applies to JS resources, indicates whether to inject elements within the HTML head element or just before the closing body tag.
-
The following example shows an external with all the configuration options:
- externals: {
+
When you create a SKY UX project, the SKY UX template provides a skyuxconfig.json file in your project's directory to enable you to configure settings for SKY UX. All properties in skyuxconfig.json are optional, and by default, the file only specifies values for the mode and compileMode settings.
The configuration options that are available in the skyuxconfig.json file include:
+
+
name — Specifies the name of the project when running in SKY UX Host. For example, if you specify "demo" as the name, then your SPA is accessible from https://host.nxt.blackbaud.com/demo. By default, SKY UX Builder uses the name property in the package.json file, minus the "blackbaud-skyux-spa-" prefix. That value is based on the root directory name that you specify when you create a SKY UX project. You can use the name property in skyuxconfig.json to overwrite the default name if you do not want the root directory name in your public-facing URL. For example, if you publish the SPA to NPM, you use the name property in package.json, so you may want to use a different name for the project in SKY UX Host.
+
mode — Controls how much boilerplate code to generate automatically. By default, the skyuxconfig.json file sets this property to easy. In easy mode, SKY UX automatically generates routes based the project's folder structure, provides bootstrapping to initialize the application, and supplies SKY UX components to the application. To override these options, change the mode property to advanced. Since advanced mode does not provide automatic route configuration, you can adjust URLs and point to specific components in your SPA. This property will eventually become obsolete as the SKY UX team builds out the CLI to allow users to override individual steps without switching to advanced mode.
+
compileMode — Specifies how to compile the project's code. By default, the skyuxconfig.json file sets this property to aot to specify ahead-of-time compilation, which runs the compiler once at build time using one set of libraries. You can change this setting to jit to specify just-in-time compilation, which runs the compiler at run time for every user using a different set of libraries. SKY UX uses ahead-of-time compilation by default because it is typically more performant and has less to handle at run-time. For more information about the difference, see the Angular documentation
+
appSettings — Specifies data that is available for reuse throughout the application. The data type of this property is any, so it accepts strings, arrays, and any other type of configuration setting that is necessary. After you specify data in the appSettings property, you can access that data throughout the SPA.
+
host — Specifies configuration options for communication with SKY UX Host.
+
+
url — Specifies a base URL to pass information from skyux serve to the SKY UX Host. The default value is https://host.nxt.blackbaud.com.
+
+
+
app — Specifies configuration options for the local app when running skyux serve.
+
+
title — Controls the page title at the template level while waiting for the application to load. After it loads, you can use the Angular title service to set the page title. The default is Blackbaud - SKY UX Application, so if you do not want this default to appear in the title bar while the application loads, use the title to provide your preferred title.
+
port — Configures a port for the skyux serve command to use. If no port is specified, skyux serve dynamically finds an available port.
+
externals — Dynamically injects CSS and JavaScript files into SKY UX Host. You should have a specific use-case for an external. For example, Office Addins require their library to load through CDN in the head. The before and after sections of an external indicate whether to include the external resource before or after the default SKY UX Builder resources. The head property, which only applies to JS resources, indicates whether to inject elements within the HTML head element or just before the closing body tag.
+
The following example shows an external with all the configuration options:
auth — Indicates whether the application requires an authenticated Blackbaud ID. This property is for internal Blackbaud use only. By default, this property is set to false. To require authentication, set this property to true. For Blackbaud developers, the helpers reference provides information about how to make authenticated HTTP requests.
-
omnibar — Specifies an object to pass to the omnibar's load method. This property is for internal Blackbaud use only. The omnibar provides a common UI element for Blackbaud applications that allows users to navigate between applications. For Blackbaud developers, the omnibar configuration options documentation describes the available options to pass to the omnibar.
-
help — Indicates whether to automatically include the help widget in the application to identify the current page and display relevant help content. This property is for internal Blackbaud use only. By default, this property is set to false. For Blackbaud developers who want to include the help widget, the help widget configuration options documentation describes the configuration object to set the help property to.
-
-
+
+
+
auth — Indicates whether the application requires an authenticated Blackbaud ID. This property is for internal Blackbaud use only. By default, this property is set to false. To require authentication, set this property to true. For Blackbaud developers, the helpers reference provides information about how to make authenticated HTTP requests.
+
omnibar — Specifies an object to pass to the omnibar's load method. This property is for internal Blackbaud use only. The omnibar provides a common UI element for Blackbaud applications that allows users to navigate between applications. For Blackbaud developers, the omnibar configuration options documentation describes the available options to pass to the omnibar.
+
help — Indicates whether to automatically include the help widget in the application to identify the current page and display relevant help content. This property is for internal Blackbaud use only. By default, this property is set to false. For Blackbaud developers who want to include the help widget, the help widget configuration options documentation describes the configuration object to set the help property to.
+
diff --git a/src/modules/list-toolbar/list-toolbar.component.spec.ts b/src/modules/list-toolbar/list-toolbar.component.spec.ts
index 6f8f57621..7298e1c7d 100644
--- a/src/modules/list-toolbar/list-toolbar.component.spec.ts
+++ b/src/modules/list-toolbar/list-toolbar.component.spec.ts
@@ -130,6 +130,24 @@ describe('List Toolbar Component', () => {
});
});
}));
+
+ it('should not set pagination to first page when pagination is undefined', async(() => {
+ initializeToolbar();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ component.toolbar.searchComponent.applySearchText('something');
+ fixture.detectChanges();
+ state.take(1).subscribe((s) => {
+ expect(s.search.searchText).toBe('something');
+ expect(s.paging.pageNumber).not.toBe(1);
+ });
+ fixture.detectChanges();
+ });
+ });
+ }));
});
describe('sort selector', () => {
diff --git a/src/modules/list-toolbar/list-toolbar.component.ts b/src/modules/list-toolbar/list-toolbar.component.ts
index c316f9681..b003110e1 100644
--- a/src/modules/list-toolbar/list-toolbar.component.ts
+++ b/src/modules/list-toolbar/list-toolbar.component.ts
@@ -248,11 +248,15 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit {
}
public updateSearchText(searchText: string) {
+ this.state.take(1).subscribe((currentState) => {
+ this.dispatcher.searchSetText(searchText);
+ if (currentState.paging.pageNumber && currentState.paging.pageNumber !== 1) {
+ this.dispatcher.next(
+ new ListPagingSetPageNumberAction(Number(1))
+ );
+ }
+ });
- this.dispatcher.searchSetText(searchText);
- this.dispatcher.next(
- new ListPagingSetPageNumberAction(Number(1))
- );
}
private itemIsInView(itemView: string, activeView: string) {
diff --git a/src/modules/text-expand-repeater/text-expand-repeater-adapter.service.ts b/src/modules/text-expand-repeater/text-expand-repeater-adapter.service.ts
index 854be1d18..cd18dc366 100644
--- a/src/modules/text-expand-repeater/text-expand-repeater-adapter.service.ts
+++ b/src/modules/text-expand-repeater/text-expand-repeater-adapter.service.ts
@@ -26,6 +26,6 @@ export class SkyTextExpandRepeaterAdapterService {
}
public setContainerHeight(containerEl: ElementRef, height: string) {
- this.renderer.setElementStyle(containerEl.nativeElement, 'height', height);
+ this.renderer.setElementStyle(containerEl.nativeElement, 'max-height', height);
}
}
diff --git a/src/modules/text-expand-repeater/text-expand-repeater.component.scss b/src/modules/text-expand-repeater/text-expand-repeater.component.scss
index 2ca93f6c3..11e6bd3a9 100644
--- a/src/modules/text-expand-repeater/text-expand-repeater.component.scss
+++ b/src/modules/text-expand-repeater/text-expand-repeater.component.scss
@@ -2,7 +2,7 @@
overflow-y: hidden;
height: auto;
margin-bottom: 0;
- transition: height 250ms;
+ transition: max-height 250ms;
}
.sky-text-expand-repeater-see-more {
white-space: nowrap;
diff --git a/src/modules/text-expand-repeater/text-expand-repeater.component.spec.ts b/src/modules/text-expand-repeater/text-expand-repeater.component.spec.ts
index 69cce8878..a7bedc240 100644
--- a/src/modules/text-expand-repeater/text-expand-repeater.component.spec.ts
+++ b/src/modules/text-expand-repeater/text-expand-repeater.component.spec.ts
@@ -1,5 +1,6 @@
import {
- TestBed
+ TestBed,
+ async
} from '@angular/core/testing';
import { BrowserModule } from '@angular/platform-browser';
@@ -10,12 +11,6 @@ import { SkyResources } from '../resources/resources';
describe('Text expand repeater component', () => {
- function createTransition() {
- let evt = document.createEvent('CustomEvent');
- evt.initEvent('transitionend', false, false);
- return evt;
- }
-
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
@@ -141,7 +136,7 @@ describe('Text expand repeater component', () => {
expect(seeMoreButton).toBeNull();
});
- it('should expand and collapse correctly', () => {
+ it('should expand and collapse correctly', async(() => {
let fixture = TestBed.createComponent(TextExpandRepeaterTestComponent);
let cmp = fixture.componentInstance as TextExpandRepeaterTestComponent;
let el = fixture.nativeElement as HTMLElement;
@@ -151,43 +146,58 @@ describe('Text expand repeater component', () => {
cmp.data = ['john', 'bob', 'hank'];
cmp.numItems = 2;
+ let shownItemsSelector = '.sky-text-expand-repeater-item:not([style*="display: none"])';
+ let hiddenItemsSelector = '.sky-text-expand-repeater-item[style*="display: none"]';
+
fixture.detectChanges();
let seeMoreButton: any = el.querySelector('.sky-text-expand-repeater-see-more');
let shownItems: any =
- el.querySelectorAll('.sky-text-expand-repeater-item:not([style*="display: none"])');
+ el.querySelectorAll(shownItemsSelector);
let hiddenItems: any =
- el.querySelectorAll('.sky-text-expand-repeater-item[style*="display: none"]');
+ el.querySelectorAll(hiddenItemsSelector);
expect(seeMoreButton).not.toBeNull();
expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_more'));
expect(shownItems.length).toBe(2);
expect(hiddenItems.length).toBe(1);
seeMoreButton.click();
- container.dispatchEvent(createTransition());
- fixture.detectChanges();
-
- shownItems =
- el.querySelectorAll('.sky-text-expand-repeater-item:not([style*="display: none"])');
- hiddenItems = el.querySelectorAll('.sky-text-expand-repeater-item[style*="display: none"]');
- seeMoreButton = el.querySelector('.sky-text-expand-repeater-see-more');
- expect(container.style.height).toBe('auto');
- expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_less'));
- expect(shownItems.length).toBe(3);
- expect(hiddenItems.length).toBe(0);
-
- seeMoreButton.click();
- container.dispatchEvent(createTransition());
fixture.detectChanges();
- shownItems =
- el.querySelectorAll('.sky-text-expand-repeater-item:not([style*="display: none"])');
- hiddenItems = el.querySelectorAll('.sky-text-expand-repeater-item[style*="display: none"]');
- seeMoreButton = el.querySelector('.sky-text-expand-repeater-see-more');
- expect(container.style.height).toBe('auto');
- expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_more'));
- expect(shownItems.length).toBe(2);
- expect(hiddenItems.length).toBe(1);
- });
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ shownItems =
+ el.querySelectorAll(shownItemsSelector);
+ hiddenItems =
+ el.querySelectorAll(hiddenItemsSelector);
+ seeMoreButton = el.querySelector('.sky-text-expand-repeater-see-more');
+ expect(container.style.maxHeight).toBe('');
+ expect(seeMoreButton.innerText.trim())
+ .toBe(SkyResources.getString('text_expand_see_less'));
+ expect(shownItems.length).toBe(3);
+ expect(hiddenItems.length).toBe(0);
+
+ seeMoreButton.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ shownItems =
+ el.querySelectorAll(shownItemsSelector);
+ hiddenItems = el.querySelectorAll(hiddenItemsSelector);
+ seeMoreButton = el.querySelector('.sky-text-expand-repeater-see-more');
+ expect(container.style.minHeight).toBe('');
+ expect(seeMoreButton.innerText.trim())
+ .toBe(SkyResources.getString('text_expand_see_more'));
+ expect(shownItems.length).toBe(2);
+ expect(hiddenItems.length).toBe(1);
+ });
+ });
+ });
+ });
+ }), 300000);
it('should not display anything if no value is given for the text', () => {
let fixture = TestBed.createComponent(TextExpandRepeaterTestComponent);
let cmp = fixture.componentInstance as TextExpandRepeaterTestComponent;
diff --git a/src/modules/text-expand-repeater/text-expand-repeater.component.ts b/src/modules/text-expand-repeater/text-expand-repeater.component.ts
index bb3cf8159..43398129f 100644
--- a/src/modules/text-expand-repeater/text-expand-repeater.component.ts
+++ b/src/modules/text-expand-repeater/text-expand-repeater.component.ts
@@ -58,22 +58,51 @@ export class SkyTextExpandRepeaterComponent implements AfterViewInit {
}
}
// Set height back to auto so the browser can change the height as needed with window changes
- this.textExpandRepeaterAdapter.setContainerHeight(this.containerEl, 'auto');
+ this.textExpandRepeaterAdapter.setContainerHeight(this.containerEl, undefined);
}
public repeaterExpand() {
+ if (!this.isExpanded) {
+ this.setContainerMaxHeight();
+ setTimeout(() => {
+ this.isExpanded = true;
+ this.animateRepeater(true);
+ });
+
+ } else {
+ this.setContainerMaxHeight();
+ setTimeout(() => {
+ this.isExpanded = false;
+ this.animateRepeater(false);
+ });
+
+ }
+ }
+
+ private setContainerMaxHeight() {
+ // ensure everything is reset
+ this.animationEnd();
+ /* Before animation is kicked off, ensure that a maxHeight exists */
+ /* Once we have support for angular v4 animations with parameters we can use that instead */
+ let currentHeight = this.textExpandRepeaterAdapter.getContainerHeight(this.containerEl);
+ this.textExpandRepeaterAdapter.setContainerHeight(this.containerEl, `${currentHeight}px`);
+ }
+
+ private animateRepeater(expanding: boolean) {
let adapter = this.textExpandRepeaterAdapter;
let container = this.containerEl;
+
+ adapter.setContainerHeight(container, undefined);
let currentHeight = adapter.getContainerHeight(container);
for (let i = this.maxItems; i < this.contentItems.length; i++) {
- if (this.isExpanded) {
+ if (!expanding) {
adapter.hideItem(this.items[i]);
} else {
adapter.showItem(this.items[i]);
}
}
let newHeight = adapter.getContainerHeight(container);
- if (this.isExpanded) {
+ if (!expanding) {
this.buttonText = this.seeMoreText;
} else {
this.buttonText = this.seeLessText;
@@ -85,13 +114,17 @@ export class SkyTextExpandRepeaterComponent implements AfterViewInit {
adapter.showItem(this.items[i]);
}
}
+
adapter.setContainerHeight(container, `${currentHeight}px`);
// This timeout is necessary due to the browser needing to pick up the non-auto height being set
// in order to do the transtion in height correctly. Without it the transition does not fire.
- setTimeout(function () {
+ setTimeout(() => {
adapter.setContainerHeight(container, `${newHeight}px`);
- }, 5);
- this.isExpanded = !this.isExpanded;
+ /* This resets values if the transition does not get kicked off */
+ setTimeout(() => {
+ this.animationEnd();
+ }, 500);
+ }, 10);
}
private setup(value: Array) {
diff --git a/src/modules/text-expand/text-expand-adapter.service.ts b/src/modules/text-expand/text-expand-adapter.service.ts
index 70789ec48..3cb92411d 100644
--- a/src/modules/text-expand/text-expand-adapter.service.ts
+++ b/src/modules/text-expand/text-expand-adapter.service.ts
@@ -14,7 +14,7 @@ export class SkyTextExpandAdapterService {
}
public setContainerHeight(containerEl: ElementRef, height: string) {
- this.renderer.setElementStyle(containerEl.nativeElement, 'height', height);
+ this.renderer.setElementStyle(containerEl.nativeElement, 'max-height', height);
}
public setText(textEl: ElementRef, text: string) {
diff --git a/src/modules/text-expand/text-expand.component.html b/src/modules/text-expand/text-expand.component.html
index ec6df47b4..fdddbf07a 100644
--- a/src/modules/text-expand/text-expand.component.html
+++ b/src/modules/text-expand/text-expand.component.html
@@ -1,4 +1,7 @@
-
+
...
diff --git a/src/modules/text-expand/text-expand.component.scss b/src/modules/text-expand/text-expand.component.scss
index 2bb6a6c3c..4cd218daa 100644
--- a/src/modules/text-expand/text-expand.component.scss
+++ b/src/modules/text-expand/text-expand.component.scss
@@ -22,7 +22,7 @@
word-wrap: break-word;
overflow: hidden;
height: auto;
- transition: height 250ms;
+ transition: max-height 250ms;
}
.sky-text-expand-modal-content {
diff --git a/src/modules/text-expand/text-expand.component.spec.ts b/src/modules/text-expand/text-expand.component.spec.ts
index b9d3b917f..558fc2223 100644
--- a/src/modules/text-expand/text-expand.component.spec.ts
+++ b/src/modules/text-expand/text-expand.component.spec.ts
@@ -1,6 +1,7 @@
import {
TestBed,
- inject
+ inject,
+ async
} from '@angular/core/testing';
import { BrowserModule } from '@angular/platform-browser';
@@ -16,12 +17,6 @@ import {
describe('Text expand component', () => {
- function createTransition() {
- let evt = document.createEvent('CustomEvent');
- evt.initEvent('transitionend', false, false);
- return evt;
- }
-
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
@@ -217,7 +212,7 @@ describe('Text expand component', () => {
expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_more'));
});
- it('should expand on click of the see more button', () => {
+ it('should expand on click of the see more button', async(() => {
let fixture = TestBed.createComponent(TextExpandTestComponent);
let cmp = fixture.componentInstance as TextExpandTestComponent;
let el = fixture.nativeElement as HTMLElement;
@@ -238,30 +233,38 @@ describe('Text expand component', () => {
expect(textArea.innerText.trim()).toBe(collapsedText);
seeMoreButton.click();
- document.querySelector('.sky-text-expand-container')
- .dispatchEvent(createTransition());
- fixture.detectChanges();
- ellipsis = el.querySelector('.sky-text-expand-ellipsis');
- textArea = el.querySelector('.sky-text-expand-text');
-
- expect(container.style.height).toBe('auto');
- expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_less'));
- expect(ellipsis).toBeNull();
- expect(textArea.innerText.trim()).toBe(expandedText);
-
- seeMoreButton.click();
- document.querySelector('.sky-text-expand-container')
- .dispatchEvent(createTransition());
fixture.detectChanges();
-
- ellipsis = el.querySelector('.sky-text-expand-ellipsis');
- textArea = el.querySelector('.sky-text-expand-text');
-
- expect(container.style.height).toBe('auto');
- expect(seeMoreButton.innerText.trim()).toBe(SkyResources.getString('text_expand_see_more'));
- expect(ellipsis).not.toBeNull();
- expect(textArea.innerText.trim()).toBe(collapsedText);
- });
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ ellipsis = el.querySelector('.sky-text-expand-ellipsis');
+ textArea = el.querySelector('.sky-text-expand-text');
+
+ expect(container.style.maxHeight).toBe('');
+ expect(seeMoreButton.innerText.trim())
+ .toBe(SkyResources.getString('text_expand_see_less'));
+ expect(ellipsis).toBeNull();
+ expect(textArea.innerText.trim()).toBe(expandedText);
+ seeMoreButton.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ ellipsis = el.querySelector('.sky-text-expand-ellipsis');
+ textArea = el.querySelector('.sky-text-expand-text');
+
+ expect(container.style.maxHeight).toBe('');
+ expect(seeMoreButton.innerText.trim())
+ .toBe(SkyResources.getString('text_expand_see_more'));
+ expect(ellipsis).not.toBeNull();
+ expect(textArea.innerText.trim()).toBe(collapsedText);
+ });
+ });
+ });
+ });
+ }), 300000);
});
describe('modal behaviors', () => {
diff --git a/src/modules/text-expand/text-expand.component.ts b/src/modules/text-expand/text-expand.component.ts
index b30af56b4..d7a26694b 100644
--- a/src/modules/text-expand/text-expand.component.ts
+++ b/src/modules/text-expand/text-expand.component.ts
@@ -21,6 +21,7 @@ import {
import {
SkyTextExpandAdapterService
} from './text-expand-adapter.service';
+
@Component({
selector: 'sky-text-expand',
templateUrl: './text-expand.component.html',
@@ -47,6 +48,7 @@ export class SkyTextExpandComponent implements AfterContentInit {
public containerEl: ElementRef;
@ViewChild('text')
public textEl: ElementRef;
+
public maxNewlines: number = 1;
public isExpanded: boolean = false;
public expandable: boolean;
@@ -84,13 +86,20 @@ export class SkyTextExpandComponent implements AfterContentInit {
} else {
// Normal View
if (!this.isExpanded) {
- this
- .animateText(this.collapsedText, this.expandedText, true);
- this.isExpanded = true;
+ this.setContainerMaxHeight();
+ setTimeout(() => {
+ this.isExpanded = true;
+ this
+ .animateText(this.collapsedText, this.expandedText, true);
+ }, 10);
+
} else {
- this
- .animateText(this.expandedText, this.collapsedText, false);
- this.isExpanded = false;
+ this.setContainerMaxHeight();
+ setTimeout(() => {
+ this.isExpanded = false;
+ this
+ .animateText(this.expandedText, this.collapsedText, false);
+ }, 10);
}
}
}
@@ -99,13 +108,22 @@ export class SkyTextExpandComponent implements AfterContentInit {
// Ensure the correct text is displayed
this.textExpandAdapter.setText(this.textEl, this.textToShow);
// Set height back to auto so the browser can change the height as needed with window changes
- this.textExpandAdapter.setContainerHeight(this.containerEl, 'auto');
+ this.textExpandAdapter.setContainerHeight(this.containerEl, undefined);
}
public ngAfterContentInit() {
this.setup(this.expandedText);
}
+ private setContainerMaxHeight() {
+ // ensure everything is reset
+ this.animationEnd();
+ /* Before animation is kicked off, ensure that a maxHeight exists */
+ /* Once we have support for angular v4 animations with parameters we can use that instead */
+ let currentHeight = this.textExpandAdapter.getContainerHeight(this.containerEl);
+ this.textExpandAdapter.setContainerHeight(this.containerEl, `${currentHeight}px`);
+ }
+
private setup(value: string) {
if (value) {
this.newlineCount = this.getNewlineCount(value);
@@ -156,8 +174,11 @@ export class SkyTextExpandComponent implements AfterContentInit {
private animateText(previousText: string, newText: string,
expanding: boolean) {
+
let adapter = this.textExpandAdapter;
let container = this.containerEl;
+ // Reset max height
+ adapter.setContainerHeight(container, undefined);
// Measure the current height so we can animate from it.
let currentHeight = adapter.getContainerHeight(container);
this.textToShow = newText;
@@ -170,11 +191,16 @@ export class SkyTextExpandComponent implements AfterContentInit {
// the collapse animation to avoid showing a big chunk of whitespace.
adapter.setText(this.textEl, previousText);
}
+
adapter.setContainerHeight(container, `${currentHeight}px`);
// This timeout is necessary due to the browser needing to pick up the non-auto height being set
// in order to do the transtion in height correctly. Without it the transition does not fire.
- setTimeout(function () {
+ setTimeout(() => {
adapter.setContainerHeight(container, `${newHeight}px`);
- }, 5);
+ /* This resets values if the transition does not get kicked off */
+ setTimeout(() => {
+ this.animationEnd();
+ }, 500);
+ }, 10);
}
}
diff --git a/src/modules/timepicker/timepicker.component.ts b/src/modules/timepicker/timepicker.component.ts
index b70dffe53..2cac62473 100644
--- a/src/modules/timepicker/timepicker.component.ts
+++ b/src/modules/timepicker/timepicker.component.ts
@@ -82,7 +82,7 @@ export class SkyTimepickerComponent implements OnInit {
/* istanbul ignore else */
/* sanity check */
if (newTime.local !== 'Invalid date') {
- this.activeTime = newTime.ios8601;
+ this.activeTime = newTime.iso8601;
}
}
}
@@ -100,7 +100,7 @@ export class SkyTimepickerComponent implements OnInit {
minute: moment(this.activeTime).minute(),
meridie: moment(this.activeTime).format('A'),
timezone: moment(this.activeTime).format('Z'),
- ios8601: this.activeTime,
+ iso8601: this.activeTime,
local: moment(this.activeTime).format(this.localeFormat),
customFormat: (typeof this.returnFormat !== 'undefined')
? this.returnFormat : this.localeFormat
diff --git a/src/modules/timepicker/timepicker.directive.ts b/src/modules/timepicker/timepicker.directive.ts
index b03ec8987..6551b034b 100644
--- a/src/modules/timepicker/timepicker.directive.ts
+++ b/src/modules/timepicker/timepicker.directive.ts
@@ -153,7 +153,7 @@ export class SkyTimepickerInputDirective implements
'minute': moment(time, currentFormat).minute(),
'meridie': moment(time, currentFormat).format('A'),
'timezone': moment(time, currentFormat).format('Z'),
- 'ios8601': moment(time, currentFormat).format(),
+ 'iso8601': moment(time, currentFormat).format(),
'local': moment(time, currentFormat).format(currentFormat),
'customFormat': this.returnFormat
};
diff --git a/src/modules/timepicker/timepicker.interface.ts b/src/modules/timepicker/timepicker.interface.ts
index 9ec29d4c3..418438ff0 100644
--- a/src/modules/timepicker/timepicker.interface.ts
+++ b/src/modules/timepicker/timepicker.interface.ts
@@ -3,7 +3,7 @@ export interface SkyTimepickerTimeOutput {
minute: number;
meridie: string;
timezone: number;
- ios8601: Date;
+ iso8601: Date;
local: string;
customFormat: string;
}