Skip to content
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

insertCssBefore option to insert esri css before any node using a selector #161

Merged
merged 4 commits into from Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -4,7 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- option to insert CSS link before an existing link tag
- `beforeCss` option to insert CSS link before an existing element
### Changed
### Fixed
### Removed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -21,7 +21,7 @@
"precompile": "npm run lint",
"prepublish": "npm run build:release",
"preversion": "npm run test && git add README.md CHANGELOG.md",
"start": "npm run clean && npm run build && concurrently \"onchange 'src/esri-loader.ts' -- npm run build\" \"karma start\"",
"start": "npm run clean && npm run build && concurrently \"onchange 'src/**/*.ts' -- npm run build\" \"karma start\"",
"test": "npm run build:release && karma start --single-run=true --browsers Firefox"
},
"repository": {
Expand Down
17 changes: 8 additions & 9 deletions src/esri-loader.ts
Expand Up @@ -10,7 +10,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ILoadCssOptions, loadCss } from './utils/css';
import { loadCss } from './utils/css';

const isBrowser = typeof window !== 'undefined';
const DEFAULT_URL = 'https://js.arcgis.com/4.10/';
Expand Down Expand Up @@ -61,8 +61,9 @@ function handleScriptError(script, callback) {
// interfaces
export interface ILoadScriptOptions {
url?: string;
css?: string | ILoadCssOptions;
css?: string;
dojoConfig?: { [propName: string]: any };
beforeCss?: string;
}

// allow consuming libraries to provide their own Promise implementations
Expand All @@ -84,9 +85,7 @@ export function isLoaded() {
// load the ArcGIS API on the page
export function loadScript(options: ILoadScriptOptions = {}): Promise<HTMLScriptElement> {
// default options
if (!options.url) {
options.url = DEFAULT_URL;
}
const url = options.url || DEFAULT_URL;

return new utils.Promise((resolve, reject) => {
let script = getScript();
Expand All @@ -95,7 +94,7 @@ export function loadScript(options: ILoadScriptOptions = {}): Promise<HTMLScript
// NOTE: have to test against scr attribute value, not script.src
// b/c the latter will return the full url for relative paths
const src = script.getAttribute('src');
if (src !== options.url) {
if (src !== url) {
// potentially trying to load a different version of the API
reject(new Error(`The ArcGIS API for JavaScript is already loaded (${src}).`));
} else {
Expand All @@ -116,15 +115,15 @@ export function loadScript(options: ILoadScriptOptions = {}): Promise<HTMLScript
// this is the first time attempting to load the API
if (options.css) {
// load the css before loading the script
loadCss(options.css);
loadCss(options.css, options.beforeCss);
}
if (options.dojoConfig) {
// set dojo configuration parameters before loading the script
window['dojoConfig'] = options.dojoConfig;
}
// create a script object whose source points to the API
script = createScript(options.url);
_currentUrl = options.url;
script = createScript(url);
_currentUrl = url;
// once the script is loaded...
handleScriptLoad(script, () => {
// update the status of the script
Expand Down
26 changes: 9 additions & 17 deletions src/utils/css.ts
@@ -1,18 +1,16 @@

function createStylesheetLink(url) {
function createStylesheetLink(href: string): HTMLLinkElement {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.href = href;
return link;
}

function insertLink(link, before?) {
// if we need to insert before an existing link, get all link tags
const allLinks = (before || before === 0) && document.getElementsByTagName('link');
if (allLinks) {
// insert the link before the link tag
const beforeLink = allLinks[before];
beforeLink.parentNode.insertBefore(link, beforeLink);
function insertLink(link: HTMLLinkElement, before?: string) {
if (before) {
// the link should be inserted before a specific node
const beforeNode = document.querySelector(before);
beforeNode.parentNode.insertBefore(link, beforeNode);
} else {
// append the link to then end of the head tag
document.head.appendChild(link);
Expand All @@ -24,19 +22,13 @@ function getCss(url) {
return document.querySelector(`link[href*="${url}"]`) as HTMLLinkElement;
}

export interface ILoadCssOptions {
url: string;
before?: number;
}

// lazy load the CSS needed for the ArcGIS API
export function loadCss(css: string | ILoadCssOptions) {
const url = typeof css === 'string' ? css : css.url;
export function loadCss(url: string, before?: string) {
let link = getCss(url);
if (!link) {
// create & load the css link
link = createStylesheetLink(url);
insertLink(link, (css as ILoadCssOptions).before);
insertLink(link, before);
}
return link;
}
19 changes: 13 additions & 6 deletions test/esri-loader.spec.js
Expand Up @@ -74,22 +74,29 @@ describe('esri-loader', function () {
});
});
});
describe('when inserting before an existing link', function () {
describe('when inserting before an existing node', function () {
var url = 'https://js.arcgis.com/4.10/esri/css/main.css';
// insert before the first <style> tag
var before = 'style';
var link;
var mockBeforeLink = {
parentNode: {
insertBefore: function (node, beforeNode) {}
}
}
beforeAll(function () {
// spyOn(document, 'querySelector');
spyOn(document, 'getElementsByTagName').and.returnValue([mockBeforeLink]);
spyOn(document, 'querySelector').and.callFake(function (selector) {
if (selector === before) {
return mockBeforeLink;
} else {
return null;
}
});
spyOn(mockBeforeLink.parentNode, 'insertBefore');
link = esriLoader.loadCss({url, before: 0});
link = esriLoader.loadCss(url, before);
});
it('should have queried all the links', function () {
expect(document.getElementsByTagName.calls.argsFor(0)[0]).toEqual(`link`);
it('should have queried for the selector', function () {
expect(document.querySelector.calls.argsFor(1)[0]).toEqual(before);
});
it('should have inserted before the mock node', function () {
expect(mockBeforeLink.parentNode.insertBefore.calls.argsFor(0)[0]).toEqual(link);
Expand Down