fix(dashboard): custom css should be removed on unmount (#15025)

* fix(dashboard): custom css should be removed on unmount

* better comment

* remove unnecessary typecast

* move type to top level scope
This commit is contained in:
David Aaron Suddjian 2021-06-07 14:04:56 -07:00 committed by GitHub
parent 422c32cb7d
commit cf15fe0d03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 33 deletions

View File

@ -26,7 +26,6 @@ import {
useDashboardDatasets,
} from 'src/common/hooks/apiResources';
import { ResourceStatus } from 'src/common/hooks/apiResources/apiResources';
import { usePrevious } from 'src/common/hooks/usePrevious';
import { hydrateDashboard } from 'src/dashboard/actions/hydrate';
import injectCustomCss from 'src/dashboard/util/injectCustomCss';
@ -42,14 +41,10 @@ const DashboardContainer = React.lazy(
const DashboardPage: FC = () => {
const dispatch = useDispatch();
const { idOrSlug } = useParams<{ idOrSlug: string }>();
const [isLoaded, setLoaded] = useState(false);
const [isHydrated, setHydrated] = useState(false);
const dashboardResource = useDashboard(idOrSlug);
const chartsResource = useDashboardCharts(idOrSlug);
const datasetsResource = useDashboardDatasets(idOrSlug);
const isLoading = [dashboardResource, chartsResource, datasetsResource].some(
resource => resource.status === ResourceStatus.LOADING,
);
const wasLoading = usePrevious(isLoading);
const error = [dashboardResource, chartsResource, datasetsResource].find(
resource => resource.status === ResourceStatus.ERROR,
)?.error;
@ -57,16 +52,22 @@ const DashboardPage: FC = () => {
useEffect(() => {
if (dashboardResource.result) {
document.title = dashboardResource.result.dashboard_title;
if (dashboardResource.result.css) {
// returning will clean up custom css
// when dashboard unmounts or changes
return injectCustomCss(dashboardResource.result.css);
}
}
return () => {};
}, [dashboardResource.result]);
const shouldBeHydrated =
dashboardResource.status === ResourceStatus.COMPLETE &&
chartsResource.status === ResourceStatus.COMPLETE &&
datasetsResource.status === ResourceStatus.COMPLETE;
useEffect(() => {
if (
wasLoading &&
dashboardResource.status === ResourceStatus.COMPLETE &&
chartsResource.status === ResourceStatus.COMPLETE &&
datasetsResource.status === ResourceStatus.COMPLETE
) {
if (shouldBeHydrated) {
dispatch(
hydrateDashboard(
dashboardResource.result,
@ -74,20 +75,13 @@ const DashboardPage: FC = () => {
datasetsResource.result,
),
);
injectCustomCss(dashboardResource.result.css);
setLoaded(true);
setHydrated(true);
}
}, [
dispatch,
wasLoading,
dashboardResource,
chartsResource,
datasetsResource,
]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldBeHydrated]);
if (error) throw error; // caught in error boundary
if (!isLoaded) return <Loading />;
if (!isHydrated) return <Loading />;
return <DashboardContainer />;
};

View File

@ -16,19 +16,31 @@
* specific language governing permissions and limitations
* under the License.
*/
export default function injectCustomCss(css) {
function createStyleElement(className: string) {
const style = document.createElement('style');
style.className = className;
style.type = 'text/css';
return style;
}
// The original, non-typescript code referenced `style.styleSheet`.
// I can't find what sort of element would have a styleSheet property,
// so have created this type to satisfy TS without changing behavior.
type MysteryStyleElement = {
styleSheet: {
cssText: string;
};
};
export default function injectCustomCss(css: string) {
const className = 'CssEditor-css';
const head = document.head || document.getElementsByTagName('head')[0];
let style = document.querySelector(`.${className}`);
const style: HTMLStyleElement =
document.querySelector(`.${className}`) || createStyleElement(className);
if (!style) {
style = document.createElement('style');
style.className = className;
style.type = 'text/css';
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
if ('styleSheet' in style) {
(style as MysteryStyleElement).styleSheet.cssText = css;
} else {
style.innerHTML = css;
}
@ -45,4 +57,8 @@ export default function injectCustomCss(css) {
*/
head.appendChild(style);
return function removeCustomCSS() {
style.remove();
};
}