Expose Panel and useLocalStorageState
Summary: Expose a Panel api from Sandy, which is quite similar to the old one, except that it uses Antd, and it will remember the users closed / open preference through sessions, a much requested feature. Reviewed By: nikoant Differential Revision: D27966607 fbshipit-source-id: 9b18df377215c1e6c5844d0bf972058c8c574cbb
This commit is contained in:
committed by
Facebook GitHub Bot
parent
faf8588097
commit
c005753018
@@ -14,7 +14,7 @@ import {Console, Hook} from 'console-feed';
|
||||
import type {Methods} from 'console-feed/lib/definitions/Methods';
|
||||
import type {Styles} from 'console-feed/lib/definitions/Styles';
|
||||
import {createState, useValue} from 'flipper-plugin';
|
||||
import {useLocalStorage} from '../utils/useLocalStorage';
|
||||
import {useLocalStorageState} from 'flipper-plugin';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {useIsDarkMode} from '../utils/useIsDarkMode';
|
||||
|
||||
@@ -66,7 +66,7 @@ const defaultLogLevels: Methods[] = ['warn', 'error', 'table', 'assert'];
|
||||
export function ConsoleLogs() {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const logs = useValue(logsAtom);
|
||||
const [logLevels, setLogLevels] = useLocalStorage<Methods[]>(
|
||||
const [logLevels, setLogLevels] = useLocalStorageState<Methods[]>(
|
||||
'console-logs-loglevels',
|
||||
defaultLogLevels,
|
||||
);
|
||||
|
||||
@@ -193,7 +193,7 @@ export {Rect} from './utils/geometry';
|
||||
export {Logger} from './fb-interfaces/Logger';
|
||||
export {getInstance as getLogger} from './fb-stubs/Logger';
|
||||
export {callVSCode, getVSCodeUrl} from './utils/vscodeUtils';
|
||||
export {useLocalStorage} from './utils/useLocalStorage';
|
||||
export {useLocalStorageState as useLocalStorage} from 'flipper-plugin';
|
||||
export {checkIdbIsInstalled} from './utils/iOSContainerUtility';
|
||||
export {IDEFileResolver, IDEType} from './fb-stubs/IDEFileResolver';
|
||||
export {renderMockFlipperWithPlugin} from './test-utils/createMockFlipperWithPlugin';
|
||||
|
||||
@@ -12,13 +12,13 @@ import {Modal, Button, Checkbox, Typography} from 'antd';
|
||||
import React, {useState} from 'react';
|
||||
import constants from '../fb-stubs/constants';
|
||||
import {NUX, Layout, theme} from 'flipper-plugin';
|
||||
import {useLocalStorage} from '../utils/useLocalStorage';
|
||||
import {useLocalStorageState} from 'flipper-plugin';
|
||||
|
||||
const {Title, Text, Link} = Typography;
|
||||
|
||||
export function SandyWelcomeScreen() {
|
||||
const [dismissed, setDismissed] = useState(false);
|
||||
const [showWelcomeScreen, setShowWelcomeScreen] = useLocalStorage(
|
||||
const [showWelcomeScreen, setShowWelcomeScreen] = useLocalStorageState(
|
||||
'flipper-sandy-show-welcome-screen',
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {render, fireEvent, act} from '@testing-library/react';
|
||||
|
||||
import {useLocalStorage} from '../useLocalStorage';
|
||||
|
||||
function TestComponent({
|
||||
storageKey,
|
||||
value,
|
||||
}: {
|
||||
storageKey: string;
|
||||
value: number;
|
||||
}) {
|
||||
const [current, setCurrent] = useLocalStorage(storageKey, value);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="value">{current}</div>
|
||||
<button
|
||||
data-testid="inc"
|
||||
onClick={() => {
|
||||
setCurrent((c) => c + 1);
|
||||
}}></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let getSpy: jest.SpyInstance;
|
||||
let setSpy: jest.SpyInstance;
|
||||
let storage: Record<string, any> = {};
|
||||
|
||||
beforeEach(() => {
|
||||
storage = {};
|
||||
getSpy = jest
|
||||
.spyOn(Storage.prototype, 'getItem') // https://github.com/facebook/jest/issues/6798#issuecomment-412871616
|
||||
.mockImplementation((key: string) => {
|
||||
return storage[key];
|
||||
});
|
||||
setSpy = jest
|
||||
.spyOn(Storage.prototype, 'setItem')
|
||||
.mockImplementation((key: string, value: any) => {
|
||||
storage[key] = value;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
getSpy.mockRestore();
|
||||
setSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('it can store values', async () => {
|
||||
const res = render(<TestComponent storageKey="x" value={1} />);
|
||||
expect((await res.findByTestId('value')).textContent).toEqual('1');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(await res.findByTestId('inc'));
|
||||
});
|
||||
|
||||
expect((await res.findByTestId('value')).textContent).toEqual('2');
|
||||
expect(storage).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"[useLocalStorage]x": "2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it can read default from storage', async () => {
|
||||
storage['[useLocalStorage]x'] = '3';
|
||||
const res = render(<TestComponent storageKey="x" value={1} />);
|
||||
expect((await res.findByTestId('value')).textContent).toEqual('3');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(await res.findByTestId('inc'));
|
||||
});
|
||||
|
||||
expect((await res.findByTestId('value')).textContent).toEqual('4');
|
||||
expect(storage).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"[useLocalStorage]x": "4",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it does not allow changing key', async () => {
|
||||
const res = render(<TestComponent storageKey="x" value={1} />);
|
||||
|
||||
expect(() => {
|
||||
const orig = console.error;
|
||||
try {
|
||||
// supress error in console
|
||||
console.error = jest.fn();
|
||||
res.rerender(<TestComponent storageKey="y" value={1} />);
|
||||
} finally {
|
||||
console.error = orig;
|
||||
}
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"The key passed to useLocalStorage should not be changed, 'x' -> 'y'"`,
|
||||
);
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {useState, useCallback} from 'react';
|
||||
|
||||
export function useLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: (() => T) | T,
|
||||
): [T, (newState: T | ((current: T) => T)) => void] {
|
||||
const [storedKey] = useState(key);
|
||||
if (storedKey !== key) {
|
||||
throw new Error(
|
||||
`The key passed to useLocalStorage should not be changed, '${storedKey}' -> '${key}'`,
|
||||
);
|
||||
}
|
||||
// Based on https://usehooks.com/useLocalStorage/ (with minor adaptions)
|
||||
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = window.localStorage.getItem('[useLocalStorage]' + key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item
|
||||
? JSON.parse(item)
|
||||
: typeof initialValue === 'function'
|
||||
? (initialValue as any)()
|
||||
: initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = useCallback(
|
||||
(value) => {
|
||||
setStoredValue((storedValue) => {
|
||||
const nextValue =
|
||||
typeof value === 'function' ? value(storedValue) : value;
|
||||
// Save to local storage
|
||||
window.localStorage.setItem(
|
||||
'[useLocalStorage]' + key,
|
||||
JSON.stringify(nextValue),
|
||||
);
|
||||
return nextValue;
|
||||
});
|
||||
},
|
||||
[key],
|
||||
);
|
||||
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
Reference in New Issue
Block a user