diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index cf094ffc2..3e88ae961 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,24 +1,146 @@ import type { Preview } from '@storybook/react'; +import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'; import React from 'react'; -import ExtensionRoot from 'src/views/components/common/ExtensionRoot/ExtensionRoot'; const preview: Preview = { - parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, }, - }, - decorators: [ - Story => ( - - - - ), - ], + decorators: [ + Story => ( + + + + + + ), + ], }; +let localData = {}; +type ListenerFunction = ( + changes: { [key: string]: chrome.storage.StorageChange }, + areaName: chrome.storage.AreaName +) => void; +const localDataListeners = new Map< + ListenerFunction, // key to remove listener + (changes: { [key: string]: chrome.storage.StorageChange }) => void +>(); + +// mock chrome api +globalThis.chrome = { + storage: { + local: { + /** + * Removes all items from storage. + * @param callback Optional. + * Callback on success, or on failure (in which case runtime.lastError will be set). + */ + async clear() { + localData = {}; + }, + /** + * Gets one or more items from storage. + * @param keys A single key to get, list of keys to get, or a dictionary specifying default values. + * An empty list or object will return an empty result object. Pass in null to get the entire contents of storage. + * @return A Promise that resolves with an object containing items + */ + async get(keys?: string | string[] | { [key: string]: any } | null) { + if (keys === null) { + return localData; + } + if (Array.isArray(keys)) { + return keys.reduce((acc, key) => { + acc[key] = localData[key]; + return acc; + }, {} as string); // funny types + } + if (typeof keys === 'string') { + return { [keys]: localData[keys] }; + } + return keys; + }, + /** + * Gets the amount of space (in bytes) being used by one or more items. + * @param keys Optional. A single key or list of keys to get the total usage for. An empty list will return 0. Pass in null to get the total usage of all of storage. + * @param callback Callback with the amount of space being used by storage, or on failure (in which case runtime.lastError will be set). + * Parameter bytesInUse: Amount of space being used in storage, in bytes. + */ + async getBytesInUse() { + return 0; + }, + /** + * Removes one or more items from storage. + * @param keys A single key or a list of keys for items to remove. + * @param callback Optional. + * Callback on success, or on failure (in which case runtime.lastError will be set). + */ + async remove(keys: string | string[]) { + if (Array.isArray(keys)) { + keys.forEach(key => { + for (const listener of localDataListeners.values()) { + listener({ [key]: { oldValue: localData[key], newValue: undefined } }); + } + + delete localData[key]; + }); + } else { + for (const listener of localDataListeners.values()) { + listener({ [keys]: { oldValue: localData[keys], newValue: undefined } }); + } + + delete localData[keys]; + } + }, + /** + * Sets multiple items. + * @param items An object which gives each key/value pair to update storage with. Any other key/value pairs in storage will not be affected. + * Primitive values such as numbers will serialize as expected. Values with a typeof "object" and "function" will typically serialize to {}, with the exception of Array (serializes as expected), Date, and Regex (serialize using their String representation). + * @param callback Optional. + * Callback on success, or on failure (in which case runtime.lastError will be set). + */ + async set(items: { [key: string]: any }) { + for (const key in items) { + const oldValue = localData[key]; + localData[key] = JSON.parse(JSON.stringify(items[key])); + + for (const listener of localDataListeners.values()) { + listener({ [key]: { oldValue: oldValue, newValue: localData[key] } }); + } + } + }, + }, + onChanged: { + /** + * Registers an event listener callback to an event. + * @param callback Called when an event occurs. The parameters of this function depend on the type of event. + */ + addListener( + listener: ( + changes: { [key: string]: chrome.storage.StorageChange }, + areaName: chrome.storage.AreaName + ) => void + ) { + localDataListeners.set(listener, (changes: { [key: string]: chrome.storage.StorageChange }) => { + listener(changes, 'local'); + }); + }, + + /** + * Deregisters an event listener callback from an event. + * @param callback Listener that shall be unregistered. + */ + removeListener(listener: ListenerFunction) { + localDataListeners.delete(listener); + }, + }, + }, +} as typeof chrome; + export default preview;