Merge pull request #1529 from spotify/bil/on-css-loaded
TechDocs: Add onCssReady transformer
This commit is contained in:
@@ -27,6 +27,7 @@ import transformer, {
|
||||
addLinkClickListener,
|
||||
removeMkdocsHeader,
|
||||
modifyCss,
|
||||
onCssReady,
|
||||
} from '../transformers';
|
||||
import { docStorageURL } from '../../config';
|
||||
import URLFormatter from '../urlFormatter';
|
||||
@@ -133,16 +134,25 @@ export const Reader = () => {
|
||||
shadowRoot?.querySelector(parsedUrl.hash)?.scrollIntoView();
|
||||
},
|
||||
}),
|
||||
onCssReady({
|
||||
docStorageURL,
|
||||
onLoading: (dom: Element) => {
|
||||
(dom as HTMLElement).style.setProperty('opacity', '0');
|
||||
},
|
||||
onLoaded: (dom: Element) => {
|
||||
(dom as HTMLElement).style.removeProperty('opacity');
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}, [componentId, path, shadowRoot, state]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (state.value instanceof Error) return <TechDocsNotFound />;
|
||||
if (state.value instanceof Error) {
|
||||
return <TechDocsNotFound />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TechDocsPageWrapper title={componentId} subtitle={componentId}>
|
||||
<div ref={shadowDomRef} />
|
||||
</TechDocsPageWrapper>
|
||||
</>
|
||||
<TechDocsPageWrapper title={componentId} subtitle={componentId}>
|
||||
<div ref={shadowDomRef} />
|
||||
</TechDocsPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ export * from './rewriteDocLinks';
|
||||
export * from './addLinkClickListener';
|
||||
export * from './removeMkdocsHeader';
|
||||
export * from './modifyCss';
|
||||
export * from './onCssReady';
|
||||
|
||||
export type Transformer = (dom: Element) => Element;
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
FIXTURES,
|
||||
createTestShadowDom,
|
||||
mockStylesheetEventListener,
|
||||
executeStylesheetEventListeners,
|
||||
clearStylesheetEventListeners,
|
||||
} from '../../test-utils';
|
||||
import { addBaseUrl, onCssReady } from '../transformers';
|
||||
|
||||
const docStorageURL: string =
|
||||
'https://techdocs-mock-sites.storage.googleapis.com';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('onCssReady', () => {
|
||||
beforeEach(() => {
|
||||
mockStylesheetEventListener(100);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearStylesheetEventListeners();
|
||||
});
|
||||
|
||||
it('does not call onLoading and onLoaded without the addBaseUrl transformer', () => {
|
||||
const onLoading = jest.fn();
|
||||
const onLoaded = jest.fn();
|
||||
|
||||
createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, {
|
||||
transformers: [
|
||||
onCssReady({
|
||||
docStorageURL,
|
||||
onLoading,
|
||||
onLoaded,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(onLoading).not.toHaveBeenCalled();
|
||||
executeStylesheetEventListeners();
|
||||
expect(onLoaded).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls the onLoading and onLoaded correctly', () => {
|
||||
const onLoading = jest.fn();
|
||||
const onLoaded = jest.fn();
|
||||
|
||||
createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, {
|
||||
transformers: [
|
||||
addBaseUrl({
|
||||
docStorageURL,
|
||||
componentId: 'mkdocs',
|
||||
path: '',
|
||||
}),
|
||||
onCssReady({
|
||||
docStorageURL,
|
||||
onLoading,
|
||||
onLoaded,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(onLoading).toHaveBeenCalledTimes(1);
|
||||
expect(onLoading).toHaveBeenCalledWith(expect.any(Element));
|
||||
expect(onLoaded).not.toHaveBeenCalled();
|
||||
|
||||
executeStylesheetEventListeners();
|
||||
|
||||
expect(onLoaded).toHaveBeenCalledTimes(1);
|
||||
expect(onLoaded).toHaveBeenCalledWith(expect.any(Element));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Transformer } from './index';
|
||||
|
||||
type OnCssReadyOptions = {
|
||||
docStorageURL: string;
|
||||
onLoading: (dom: Element) => void;
|
||||
onLoaded: (dom: Element) => void;
|
||||
};
|
||||
|
||||
export const onCssReady = ({
|
||||
docStorageURL,
|
||||
onLoading,
|
||||
onLoaded,
|
||||
}: OnCssReadyOptions): Transformer => {
|
||||
return dom => {
|
||||
const cssPages = Array.from(
|
||||
dom.querySelectorAll('head > link[rel="stylesheet"]'),
|
||||
).filter(elem => elem.getAttribute('href')?.startsWith(docStorageURL));
|
||||
|
||||
let count = cssPages.length;
|
||||
|
||||
if (count > 0) {
|
||||
onLoading(dom);
|
||||
}
|
||||
|
||||
cssPages.forEach(cssPage =>
|
||||
cssPage.addEventListener('load', () => {
|
||||
count -= 1;
|
||||
|
||||
if (count === 0) {
|
||||
onLoaded(dom);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return dom;
|
||||
};
|
||||
};
|
||||
@@ -15,49 +15,10 @@
|
||||
*/
|
||||
|
||||
import FIXTURE_STANDARD_PAGE from './fixtures/mkdocs-index';
|
||||
import transformer from '../reader/transformers';
|
||||
import type { Transformer } from '../reader/transformers';
|
||||
|
||||
export const FIXTURES = {
|
||||
FIXTURE_STANDARD_PAGE,
|
||||
};
|
||||
|
||||
export type CreateTestShadowDomOptions = {
|
||||
transformers: Transformer[];
|
||||
};
|
||||
|
||||
export const createTestShadowDom = (
|
||||
fixture: string,
|
||||
opts: CreateTestShadowDomOptions = { transformers: [] },
|
||||
): ShadowRoot => {
|
||||
const divElement = document.createElement('div');
|
||||
divElement.attachShadow({ mode: 'open' });
|
||||
document.body.appendChild(divElement);
|
||||
|
||||
const domParser = new DOMParser().parseFromString(fixture, 'text/html');
|
||||
divElement.shadowRoot?.appendChild(domParser.documentElement);
|
||||
|
||||
if (opts.transformers) {
|
||||
transformer(divElement.shadowRoot!.children[0], opts.transformers);
|
||||
}
|
||||
|
||||
return divElement.shadowRoot!;
|
||||
};
|
||||
|
||||
export const getSample = (
|
||||
shadowDom: ShadowRoot,
|
||||
elementName: string,
|
||||
elementAttribute: string,
|
||||
sampleSize = 2,
|
||||
) => {
|
||||
const rootElement = shadowDom.children[0];
|
||||
|
||||
return Array.from(rootElement.getElementsByTagName(elementName))
|
||||
.filter(elem => {
|
||||
return elem.hasAttribute(elementAttribute);
|
||||
})
|
||||
.slice(0, sampleSize)
|
||||
.map(elem => {
|
||||
return elem.getAttribute(elementAttribute);
|
||||
});
|
||||
};
|
||||
export * from './stylesheets';
|
||||
export * from './shadowDom';
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import transformer from '../reader/transformers';
|
||||
import type { Transformer } from '../reader/transformers';
|
||||
|
||||
export type CreateTestShadowDomOptions = {
|
||||
transformers: Transformer[];
|
||||
};
|
||||
|
||||
export const createTestShadowDom = (
|
||||
fixture: string,
|
||||
opts: CreateTestShadowDomOptions = { transformers: [] },
|
||||
): ShadowRoot => {
|
||||
const divElement = document.createElement('div');
|
||||
divElement.attachShadow({ mode: 'open' });
|
||||
document.body.appendChild(divElement);
|
||||
|
||||
const domParser = new DOMParser().parseFromString(fixture, 'text/html');
|
||||
divElement.shadowRoot?.appendChild(domParser.documentElement);
|
||||
|
||||
if (opts.transformers) {
|
||||
transformer(divElement.shadowRoot!.children[0], opts.transformers);
|
||||
}
|
||||
|
||||
return divElement.shadowRoot!;
|
||||
};
|
||||
|
||||
export const getSample = (
|
||||
shadowDom: ShadowRoot,
|
||||
elementName: string,
|
||||
elementAttribute: string,
|
||||
sampleSize = 2,
|
||||
) => {
|
||||
const rootElement = shadowDom.children[0];
|
||||
|
||||
return Array.from(rootElement.getElementsByTagName(elementName))
|
||||
.filter(elem => {
|
||||
return elem.hasAttribute(elementAttribute);
|
||||
})
|
||||
.slice(0, sampleSize)
|
||||
.map(elem => {
|
||||
return elem.getAttribute(elementAttribute);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const mockStylesheetEventListener = (timeToCallbackMs: number): void => {
|
||||
HTMLLinkElement.prototype.addEventListener = (
|
||||
_eventName: string,
|
||||
eventCallback: any,
|
||||
) => {
|
||||
setTimeout(() => {
|
||||
eventCallback();
|
||||
}, timeToCallbackMs);
|
||||
};
|
||||
};
|
||||
|
||||
export const executeStylesheetEventListeners = (): void => {
|
||||
jest.runOnlyPendingTimers();
|
||||
};
|
||||
|
||||
export const clearStylesheetEventListeners = (): void => {
|
||||
HTMLLinkElement.prototype.addEventListener =
|
||||
Element.prototype.addEventListener;
|
||||
};
|
||||
Reference in New Issue
Block a user