-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
web: add zoom in/out for logs (#4354)
Add buttons next to "Clear logs" to adjust the font size for logs. At a high-level, a CSS custom property (variable) is used to set a % font size for log lines (the absolute font size is set on the container, and the default property value is 100%). This provides an easy and performant way for JS to adjust the log font size, particularly since the actual log CSS is pure CSS from an SCSS stylesheet, not using `styled-components` in React. The scale value is read from/written to local storage under a global (NOT Tiltfile-namespaced) key so that it's persistent and applies to all Tilt sessions. As a bonus, that means it live synchronizes between tabs which is kinda nifty.
- Loading branch information
Showing
6 changed files
with
204 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { mount } from "enzyme" | ||
import React from "react" | ||
import { | ||
FontSizeDecreaseButton, | ||
FontSizeIncreaseButton, | ||
LogFontSizeScaleCSSProperty, | ||
LogFontSizeScaleLocalStorageKey, | ||
LogFontSizeScaleMinimumPercentage, | ||
LogsFontSize, | ||
} from "./LogActions" | ||
|
||
describe("LogsFontSize", () => { | ||
const cleanup = () => { | ||
localStorage.clear() | ||
document.documentElement.style.removeProperty("--log-font-scale") | ||
} | ||
|
||
beforeEach(() => { | ||
cleanup() | ||
// CSS won't be loaded in test context, so just explicitly set it | ||
document.documentElement.style.setProperty("--log-font-scale", "100%") | ||
}) | ||
afterEach(cleanup) | ||
|
||
const getCSSValue = () => | ||
getComputedStyle(document.documentElement).getPropertyValue( | ||
LogFontSizeScaleCSSProperty | ||
) | ||
// react-storage-hooks JSON (de)serializes transparently, | ||
// need to do the same when directly manipulating local storage | ||
const getLocalStorageValue = () => | ||
JSON.parse(localStorage.getItem(LogFontSizeScaleLocalStorageKey) || "") | ||
const setLocalStorageValue = (val: string) => | ||
localStorage.setItem(LogFontSizeScaleLocalStorageKey, JSON.stringify(val)) | ||
|
||
it("restores persisted font scale on load", () => { | ||
setLocalStorageValue("360%") | ||
mount(<LogsFontSize />) | ||
expect(getCSSValue()).toEqual("360%") | ||
}) | ||
|
||
it("decreases font scale", () => { | ||
const root = mount(<LogsFontSize />) | ||
root.find(FontSizeDecreaseButton).simulate("click") | ||
expect(getCSSValue()).toEqual("95%") | ||
expect(getLocalStorageValue()).toEqual(`95%`) // JSON serialized | ||
}) | ||
|
||
it("has a minimum font scale", () => { | ||
setLocalStorageValue(`${LogFontSizeScaleMinimumPercentage}%`) | ||
const root = mount(<LogsFontSize />) | ||
root.find(FontSizeDecreaseButton).simulate("click") | ||
expect(getCSSValue()).toEqual("10%") | ||
expect(getLocalStorageValue()).toEqual(`10%`) | ||
}) | ||
|
||
it("increases font scale", () => { | ||
const root = mount(<LogsFontSize />) | ||
root.find(FontSizeIncreaseButton).simulate("click") | ||
expect(getCSSValue()).toEqual("105%") | ||
expect(getLocalStorageValue()).toEqual(`105%`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import React, { useEffect } from "react" | ||
import { useStorageState } from "react-storage-hooks" | ||
import styled from "styled-components" | ||
import { incr } from "./analytics" | ||
import ClearLogs from "./ClearLogs" | ||
import { | ||
AnimDuration, | ||
Color, | ||
FontSize, | ||
mixinResetButtonStyle, | ||
} from "./style-helpers" | ||
|
||
export const LogFontSizeScaleLocalStorageKey = "tilt.global.log-font-scale" | ||
export const LogFontSizeScaleCSSProperty = "--log-font-scale" | ||
export const LogFontSizeScaleMinimumPercentage = 10 | ||
|
||
const LogActionsGroup = styled.div` | ||
margin-left: auto; | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: space-between; | ||
` | ||
|
||
const FontSizeControls = styled.div` | ||
color: ${Color.gray6}; | ||
vertical-align: middle; | ||
display: flex; | ||
flex-wrap: nowrap; | ||
` | ||
|
||
const FontSizeControlsDivider = styled.div` | ||
font-size: ${FontSize.default}; | ||
user-select: none; | ||
` | ||
|
||
const FontSizeButton = styled.button` | ||
${mixinResetButtonStyle} | ||
color: ${Color.gray6}; | ||
transition: color ${AnimDuration.default} ease; | ||
padding: 0 4px; | ||
user-select: none; | ||
&:hover { | ||
color: ${Color.blue}; | ||
} | ||
` | ||
|
||
export const FontSizeDecreaseButton = styled(FontSizeButton)` | ||
font-size: ${FontSize.smallest}; | ||
` | ||
|
||
export const FontSizeIncreaseButton = styled(FontSizeButton)` | ||
font-size: ${FontSize.default}; | ||
` | ||
|
||
export const LogsFontSize: React.FC = () => { | ||
// this uses `useStorageState` directly vs `usePersistentState` wrapper because it's a global setting | ||
// (i.e. log zoom applies across all Tiltfiles) | ||
const [logFontScale, setLogFontSize] = useStorageState<string>( | ||
localStorage, | ||
LogFontSizeScaleLocalStorageKey, | ||
() => | ||
document.documentElement.style.getPropertyValue( | ||
LogFontSizeScaleCSSProperty | ||
) | ||
) | ||
useEffect(() => { | ||
if (!logFontScale?.endsWith("%")) { | ||
// somehow an invalid value ended up in local storage - reset to 100% and let effect run again | ||
setLogFontSize("100%") | ||
return | ||
} | ||
document.documentElement.style.setProperty( | ||
LogFontSizeScaleCSSProperty, | ||
logFontScale | ||
) | ||
}, [logFontScale]) | ||
|
||
const adjustLogFontScale = (step: number) => { | ||
const val = Math.max( | ||
parseFloat(logFontScale) + step, | ||
LogFontSizeScaleMinimumPercentage | ||
) | ||
setLogFontSize(`${val}%`) | ||
incr("ui.web.zoomLogs", { action: "click", dir: step < 0 ? "out" : "in" }) | ||
} | ||
|
||
const zoomStep = 5 | ||
return ( | ||
<FontSizeControls> | ||
<FontSizeDecreaseButton | ||
aria-label={"Decrease log font size"} | ||
onClick={() => adjustLogFontScale(-zoomStep)} | ||
> | ||
A | ||
</FontSizeDecreaseButton> | ||
<FontSizeControlsDivider aria-hidden={true}>|</FontSizeControlsDivider> | ||
<FontSizeIncreaseButton | ||
aria-label={"Increase log font size"} | ||
onClick={() => adjustLogFontScale(zoomStep)} | ||
> | ||
A | ||
</FontSizeIncreaseButton> | ||
</FontSizeControls> | ||
) | ||
} | ||
|
||
export interface LogActionsProps { | ||
resourceName: string | ||
isSnapshot: boolean | ||
} | ||
|
||
const LogActions: React.FC<LogActionsProps> = ({ | ||
resourceName, | ||
isSnapshot, | ||
}) => { | ||
return ( | ||
<LogActionsGroup> | ||
<LogsFontSize /> | ||
{isSnapshot || <ClearLogs resourceName={resourceName} />} | ||
</LogActionsGroup> | ||
) | ||
} | ||
|
||
export default LogActions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters