diff --git a/src/renderer/components/+preferences/application.tsx b/src/renderer/components/+preferences/application.tsx index 40859cd5e0..1e3c75b68f 100644 --- a/src/renderer/components/+preferences/application.tsx +++ b/src/renderer/components/+preferences/application.tsx @@ -27,7 +27,7 @@ import { ThemeStore } from "../../theme.store"; import { UserStore } from "../../../common/user-store"; import { Input } from "../input"; import { isWindows } from "../../../common/vars"; -import { FormSwitch, Switcher } from "../switch"; +import { Switch } from "../switch"; import moment from "moment-timezone"; import { CONSTANTS, defaultExtensionRegistryUrl, ExtensionRegistryLocation } from "../../../common/user-store/preferences-helpers"; import { action } from "mobx"; @@ -86,16 +86,12 @@ export const Application = observer(() => {
- userStore.terminalCopyOnSelect = v.target.checked} - name="terminalCopyOnSelect" - /> - } - /> + userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect} + > + Copy on select and paste on right-click +

@@ -135,16 +131,9 @@ export const Application = observer(() => {
- userStore.openAtLogin = v.target.checked} - name="startup" - /> - } - label="Automatically start Lens on login" - /> + userStore.openAtLogin = !userStore.openAtLogin}> + Automatically start Lens on login +

diff --git a/src/renderer/components/+preferences/editor.tsx b/src/renderer/components/+preferences/editor.tsx index 6f346eb63d..523f9bd278 100644 --- a/src/renderer/components/+preferences/editor.tsx +++ b/src/renderer/components/+preferences/editor.tsx @@ -21,7 +21,7 @@ import { observer } from "mobx-react"; import React from "react"; import { UserStore } from "../../../common/user-store"; -import { FormSwitch, Switcher } from "../switch"; +import { Switch } from "../switch"; import { Select } from "../select"; import { SubTitle } from "../layout/sub-title"; import { SubHeader } from "../layout/sub-header"; @@ -45,15 +45,12 @@ export const Editor = observer(() => {
- Show minimap} - control={ - editorConfiguration.minimap.enabled = checked} - /> - } - /> + editorConfiguration.minimap.enabled = !editorConfiguration.minimap.enabled} + > + Show minimap +
Position diff --git a/src/renderer/components/+preferences/kubectl-binaries.tsx b/src/renderer/components/+preferences/kubectl-binaries.tsx index bffc69e3c0..13a0299ee3 100644 --- a/src/renderer/components/+preferences/kubectl-binaries.tsx +++ b/src/renderer/components/+preferences/kubectl-binaries.tsx @@ -26,7 +26,7 @@ import { getDefaultKubectlDownloadPath, UserStore } from "../../../common/user-s import { observer } from "mobx-react"; import { bundledKubectlPath } from "../../../main/kubectl"; import { SelectOption, Select } from "../select"; -import { FormSwitch, Switcher } from "../switch"; +import { Switch } from "../switch"; import { packageMirrors } from "../../../common/user-store/preferences-helpers"; export const KubectlBinaries = observer(() => { @@ -48,16 +48,12 @@ export const KubectlBinaries = observer(() => { <>
- userStore.downloadKubectlBinaries = v.target.checked} - name="kubectl-download" - /> - } - label="Download kubectl binaries matching the Kubernetes cluster version" - /> + userStore.downloadKubectlBinaries = !userStore.downloadKubectlBinaries} + > + Download kubectl binaries matching the Kubernetes cluster version +
diff --git a/src/renderer/components/+preferences/proxy.tsx b/src/renderer/components/+preferences/proxy.tsx index b85bd5e0e1..f25c1fbc9b 100644 --- a/src/renderer/components/+preferences/proxy.tsx +++ b/src/renderer/components/+preferences/proxy.tsx @@ -24,10 +24,11 @@ import React from "react"; import { UserStore } from "../../../common/user-store"; import { Input } from "../input"; import { SubTitle } from "../layout/sub-title"; -import { FormSwitch, Switcher } from "../switch"; +import { Switch } from "../switch"; export const LensProxy = observer(() => { const [proxy, setProxy] = React.useState(UserStore.getInstance().httpsProxy || ""); + const store = UserStore.getInstance(); return (
@@ -50,16 +51,9 @@ export const LensProxy = observer(() => {
- UserStore.getInstance().allowUntrustedCAs = v.target.checked} - name="startup" - /> - } - label="Allow untrusted Certificate Authorities" - /> + store.allowUntrustedCAs = !store.allowUntrustedCAs}> + Allow untrusted Certificate Authorities + This will make Lens to trust ANY certificate authority without any validations.{" "} Needed with some corporate proxies that do certificate re-writing.{" "} diff --git a/src/renderer/components/switch/__tests__/switch.test.tsx b/src/renderer/components/switch/__tests__/switch.test.tsx new file mode 100644 index 0000000000..d5187e1417 --- /dev/null +++ b/src/renderer/components/switch/__tests__/switch.test.tsx @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import "@testing-library/jest-dom/extend-expect"; +import { Switch } from ".."; + +describe("", () => { + it("renders w/o errors", () => { + const { container } = render(); + + expect(container).toBeInstanceOf(HTMLElement); + }); + + it("render label text", () => { + const { getByLabelText } = render(Test label); + + expect(getByLabelText("Test label")).toBeTruthy(); + }); + + it("passes disabled and checked attributes to input", () => { + const { container } = render(); + const checkbox = container.querySelector("input[type=checkbox]"); + + expect(checkbox).toHaveAttribute("disabled"); + expect(checkbox).toHaveAttribute("checked"); + }); + + it("onClick event fired", () => { + const onClick = jest.fn(); + const { getByTestId } = render(); + const switcher = getByTestId("switch"); + + fireEvent.click(switcher); + + expect(onClick).toHaveBeenCalled(); + }); + + it("onClick event not fired for disabled item", () => { + const onClick = jest.fn(); + const { getByTestId } = render(); + const switcher = getByTestId("switch"); + + fireEvent.click(switcher); + + expect(onClick).not.toHaveBeenCalled(); + }); +}); diff --git a/src/renderer/components/switch/form-switcher.tsx b/src/renderer/components/switch/form-switcher.tsx index 14df95f676..6499fb0b0f 100644 --- a/src/renderer/components/switch/form-switcher.tsx +++ b/src/renderer/components/switch/form-switcher.tsx @@ -35,6 +35,9 @@ const useStyles = makeStyles({ }, }); +/** + * @deprecated Use instead from "../switch.tsx". + */ export function FormSwitch(props: FormControlLabelProps) { const classes = useStyles(); diff --git a/src/renderer/components/switch/index.ts b/src/renderer/components/switch/index.ts index 60d44f3324..6987ce5b38 100644 --- a/src/renderer/components/switch/index.ts +++ b/src/renderer/components/switch/index.ts @@ -21,3 +21,4 @@ export * from "./switcher"; export * from "./form-switcher"; +export * from "./switch"; diff --git a/src/renderer/components/switch/switch.module.scss b/src/renderer/components/switch/switch.module.scss new file mode 100644 index 0000000000..b890919c61 --- /dev/null +++ b/src/renderer/components/switch/switch.module.scss @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +.Switch { + --thumb-size: 2rem; + --thumb-color: hsl(0 0% 100%); + --thumb-color-highlight: hsl(0 0% 100% / 25%); + + --track-size: calc(var(--thumb-size) * 2); + --track-padding: 2px; + --track-color-inactive: hsl(80 0% 35%); + --track-color-active: hsl(110, 60%, 60%); + + --thumb-position: 0%; + --thumb-transition-duration: .25s; + + --hover-highlight-size: 0; + + display: flex; + align-items: center; + gap: 2ch; + justify-content: space-between; + cursor: pointer; + user-select: none; + color: var(--textColorAccent); + font-weight: 500; + + & > input { + padding: var(--track-padding); + background: var(--track-color-inactive); + inline-size: var(--track-size); + block-size: var(--thumb-size); + border-radius: var(--track-size); + + appearance: none; + pointer-events: none; + border: none; + outline-offset: 5px; + box-sizing: content-box; + + flex-shrink: 0; + display: grid; + align-items: center; + grid: [track] 1fr / [track] 1fr; + + transition: background-color .25s ease; + + &::before { + content: ""; + cursor: pointer; + pointer-events: auto; + grid-area: track; + inline-size: var(--thumb-size); + block-size: var(--thumb-size); + background: var(--thumb-color); + box-shadow: 0 0 0 var(--hover-highlight-size) var(--thumb-color-highlight); + border-radius: 50%; + transform: translateX(var(--thumb-position)); + transition: + transform var(--thumb-transition-duration) ease, + box-shadow .25s ease; + } + + &:not(:disabled):hover::before { + --hover-highlight-size: .5rem; + } + + &:checked { + background: var(--track-color-active); + --thumb-position: 100%; + } + + &:disabled { + --track-color-inactive: hsl(80 0% 30%); + --thumb-color: transparent; + + &::before { + cursor: not-allowed; + box-shadow: inset 0 0 0 2px hsl(0 0% 0% / 40%); + } + } + + &:focus-visible { + box-shadow: 0 0 0 2px var(--blue); + } + } + + &.disabled { + cursor: not-allowed; + } +} + +@include theme-light { + .Switch { + --thumb-color-highlight: hsl(0 0% 0% / 25%); + + & > input { + &:disabled { + --track-color-inactive: hsl(80 0% 80%); + } + } + } +} diff --git a/src/renderer/components/switch/switch.tsx b/src/renderer/components/switch/switch.tsx new file mode 100644 index 0000000000..7bf5f2b3be --- /dev/null +++ b/src/renderer/components/switch/switch.tsx @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import styles from "./switch.module.scss"; + +import React, { ChangeEvent, HTMLProps } from "react"; +import { cssNames } from "../../utils"; + +interface Props extends Omit, "onChange"> { + onChange?: (checked: boolean, event: ChangeEvent) => void; +} + +export function Switch({ children, disabled, onChange, ...props }: Props) { + return ( + + ); +} diff --git a/src/renderer/components/switch/switcher.tsx b/src/renderer/components/switch/switcher.tsx index 749e5134c9..136175e394 100644 --- a/src/renderer/components/switch/switcher.tsx +++ b/src/renderer/components/switch/switcher.tsx @@ -31,6 +31,9 @@ interface Props extends SwitchProps { classes: Styles; } +/** + * @deprecated Use instead from "../switch.tsx". + */ export const Switcher = withStyles((theme: Theme) => createStyles({ root: {