diff --git a/packages/business-features/keyboard-shortcuts/coverage/lcov-report/base.css b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/base.css new file mode 100644 index 0000000000..f418035b46 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/packages/business-features/keyboard-shortcuts/coverage/lcov-report/block-navigation.js b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000000..cc12130231 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/packages/business-features/keyboard-shortcuts/coverage/lcov-report/favicon.png b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000..c1525b811a Binary files /dev/null and b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/favicon.png differ diff --git a/packages/business-features/keyboard-shortcuts/coverage/lcov-report/feature.ts.html b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/feature.ts.html new file mode 100644 index 0000000000..26c268a02e --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/coverage/lcov-report/feature.ts.html @@ -0,0 +1,136 @@ + + + + +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 | 1x +1x +1x +1x +1x +1x +1x +1x +17x +17x +17x +17x +17x +17x +1x +1x +1x + | import { getFeature } from "@k8slens/feature-core";
+import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
+import { reactApplicationFeature } from "@k8slens/react-application";
+
+export const keyboardShortcutsFeature = getFeature({
+ id: "keyboard-shortcuts",
+
+ register: (di) => {
+ autoRegister({
+ di,
+ targetModule: module,
+ getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)],
+ });
+ },
+
+ dependencies: [reactApplicationFeature],
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| feature.ts | +
+
+ |
+ 100% | +17/17 | +100% | +3/3 | +100% | +3/3 | +100% | +17/17 | +
| invoke-shortcut.injectable.ts | +
+
+ |
+ 100% | +94/94 | +100% | +22/22 | +100% | +6/6 | +100% | +94/94 | +
| keyboard-shortcut-injection-token.ts | +
+
+ |
+ 100% | +22/22 | +100% | +1/1 | +100% | +1/1 | +100% | +22/22 | +
| keyboard-shortcut-listener-react-application-hoc.injectable.tsx | +
+
+ |
+ 100% | +10/10 | +100% | +2/2 | +100% | +2/2 | +100% | +10/10 | +
| keyboard-shortcut-listener.tsx | +
+
+ |
+ 100% | +41/41 | +100% | +5/5 | +100% | +3/3 | +100% | +41/41 | +
| keyboard-shortcut-scope.tsx | +
+
+ |
+ 100% | +12/12 | +100% | +2/2 | +100% | +2/2 | +100% | +12/12 | +
| platform.injectable.ts | +
+
+ |
+ 100% | +11/11 | +100% | +1/1 | +100% | +1/1 | +100% | +11/11 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +18x +18x +18x +16x +16x +16x +16x +2x +2x +2x +2x +2x +2x +2x +2x +1x +1x +122x +94x +94x +94x +94x +94x +94x +94x +94x +28x +28x +28x +28x +28x +28x +28x +1x +1x +1x +1x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +122x +22x +22x +21x +21x +122x +122x +1x +1x +1x +1x +1x +17x +17x +17x +17x +31x +31x +31x +31x +31x +31x +31x +12x +12x +31x +17x +1x +1x +1x + | import { pipeline } from "@ogre-tools/fp";
+import { filter, isString } from "lodash/fp";
+import { getInjectable } from "@ogre-tools/injectable";
+import {
+ Binding,
+ KeyboardShortcut,
+ keyboardShortcutInjectionToken,
+} from "./keyboard-shortcut-injection-token";
+import platformInjectable from "./platform.injectable";
+
+export type InvokeShortcut = (event: KeyboardEvent) => void;
+
+const toShortcutsWithMatchingScope = (shortcut: KeyboardShortcut) => {
+ const activeScopeElement = document.activeElement?.closest("[data-keyboard-shortcut-scope]");
+
+ if (!activeScopeElement) {
+ const shortcutIsRootLevel = !shortcut.scope;
+
+ return shortcutIsRootLevel;
+ }
+
+ const castedActiveScopeElementHtml = activeScopeElement as HTMLDivElement;
+
+ // eslint-disable-next-line xss/no-mixed-html
+ const activeScope = castedActiveScopeElementHtml.dataset.keyboardShortcutScope;
+
+ return shortcut.scope === activeScope;
+};
+
+const toBindingWithDefaults = (binding: Binding) =>
+ isString(binding)
+ ? {
+ code: binding,
+ shift: false,
+ ctrl: false,
+ altOrOption: false,
+ meta: false,
+ ctrlOrCommand: false,
+ }
+ : {
+ ctrl: false,
+ shift: false,
+ altOrOption: false,
+ meta: false,
+ ctrlOrCommand: false,
+ ...binding,
+ };
+
+const toShortcutsWithMatchingBinding =
+ (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => {
+ const binding = toBindingWithDefaults(shortcut.binding);
+
+ const shiftModifierMatches = binding.shift === event.shiftKey;
+ const altModifierMatches = binding.altOrOption === event.altKey;
+
+ const isMac = platform === "darwin";
+
+ const ctrlModifierMatches =
+ binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey);
+
+ const metaModifierMatches =
+ binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey);
+
+ return (
+ event.code === binding.code &&
+ shiftModifierMatches &&
+ ctrlModifierMatches &&
+ altModifierMatches &&
+ metaModifierMatches
+ );
+ };
+
+const invokeShortcutInjectable = getInjectable({
+ id: "invoke-shortcut",
+
+ instantiate: (di): InvokeShortcut => {
+ const getShortcuts = () => di.injectMany(keyboardShortcutInjectionToken);
+ const platform = di.inject(platformInjectable);
+
+ return (event) => {
+ const shortcutsToInvoke = pipeline(
+ getShortcuts(),
+ filter(toShortcutsWithMatchingBinding(event, platform)),
+ filter(toShortcutsWithMatchingScope),
+ );
+
+ if (shortcutsToInvoke.length) {
+ shortcutsToInvoke.forEach((shortcut) => shortcut.invoke());
+ }
+ };
+ },
+});
+
+export default invokeShortcutInjectable;
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x + | import { getInjectionToken } from "@ogre-tools/injectable";
+
+export type Binding =
+ | string
+ | {
+ code: string;
+ shift?: boolean;
+ ctrl?: boolean;
+ altOrOption?: boolean;
+ meta?: boolean;
+ ctrlOrCommand?: boolean;
+ };
+
+export type KeyboardShortcut = {
+ binding: Binding;
+ invoke: () => void;
+ scope?: string;
+};
+
+export const keyboardShortcutInjectionToken = getInjectionToken<KeyboardShortcut>({
+ id: "keyboard-shortcut-injection-token",
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x + | import { getInjectable } from "@ogre-tools/injectable";
+import { KeyboardShortcutListener } from "./keyboard-shortcut-listener";
+import { reactApplicationHigherOrderComponentInjectionToken } from "@k8slens/react-application";
+
+export const keyboardShortcutListenerReactApplicationHocInjectable = getInjectable({
+ id: "keyboard-shortcut-listener-react-application-hoc",
+ instantiate: () => KeyboardShortcutListener,
+
+ injectionToken: reactApplicationHigherOrderComponentInjectionToken,
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +17x +17x +17x +17x +17x +17x +17x +17x +17x +17x +17x +17x +17x +1x +1x +1x +1x +1x +1x +1x +1x +1x +17x +17x +17x +1x +1x + | import { withInjectables } from "@ogre-tools/injectable-react";
+import React, { useEffect } from "react";
+
+import invokeShortcutInjectable, { InvokeShortcut } from "./invoke-shortcut.injectable";
+
+export interface KeyboardShortcutListenerProps {
+ children: React.ReactNode;
+}
+
+interface Dependencies {
+ invokeShortcut: InvokeShortcut;
+}
+
+const NonInjectedKeyboardShortcutListener = ({
+ children,
+ invokeShortcut,
+}: KeyboardShortcutListenerProps & Dependencies) => {
+ useEffect(() => {
+ document.addEventListener("keydown", invokeShortcut);
+
+ return () => {
+ document.removeEventListener("keydown", invokeShortcut);
+ };
+ });
+
+ return <>{children}</>;
+};
+
+export const KeyboardShortcutListener = withInjectables<
+ Dependencies,
+ KeyboardShortcutListenerProps
+>(
+ NonInjectedKeyboardShortcutListener,
+
+ {
+ getProps: (di, props) => ({
+ invokeShortcut: di.inject(invokeShortcutInjectable),
+ ...props,
+ }),
+ },
+);
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 | 1x +1x +1x +1x +1x +1x +1x +1x +17x +17x +1x +1x + | import React from "react";
+
+export interface KeyboardShortcutScopeProps {
+ id: string;
+ children: React.ReactNode;
+}
+
+export const KeyboardShortcutScope = ({ id, children }: KeyboardShortcutScopeProps) => (
+ <div data-keyboard-shortcut-scope={id} data-keyboard-shortcut-scope-test={id} tabIndex={-1}>
+ {children}
+ </div>
+);
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x + | import { getInjectable } from "@ogre-tools/injectable";
+
+export const allPlatforms = ["win32", "darwin", "linux"] as const;
+
+const platformInjectable = getInjectable({
+ id: "platform",
+ instantiate: () => process.platform as (typeof allPlatforms)[number],
+ causesSideEffects: true,
+});
+
+export default platformInjectable;
+ |