mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-25 11:03:57 +03:00
Integrate color scheme preference into ui library (#950)
This commit is contained in:
parent
a8883f287d
commit
1de7e11a0a
|
@ -12,6 +12,7 @@
|
|||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"settings": {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"prettier"
|
||||
|
|
|
@ -62,12 +62,10 @@ class App extends Component<Props> {
|
|||
function mapStateToProps(state: StoreState) {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
const { themeColorPreference, ...themeData } = state.theme;
|
||||
|
||||
return {
|
||||
options: instances.options[id],
|
||||
section: state.section,
|
||||
theme: themeData,
|
||||
theme: state.theme,
|
||||
notification: state.notification,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ import configureStore from '../../../app/stores/panelStore';
|
|||
|
||||
import '../../views/devpanel.pug';
|
||||
import { Action, Store } from 'redux';
|
||||
import {
|
||||
applyMediaFeaturesPreferences,
|
||||
StoreAction,
|
||||
} from '@redux-devtools/app/lib/actions';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
import { PanelMessage } from '../../../app/middlewares/api';
|
||||
import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
@ -36,20 +33,9 @@ function renderDevTools() {
|
|||
unmountComponentAtNode(node!);
|
||||
clearTimeout(naTimeout);
|
||||
({ store, persistor } = configureStore(position, bgConnection));
|
||||
|
||||
const onBeforeLift = () => {
|
||||
if (store) {
|
||||
store.dispatch(applyMediaFeaturesPreferences());
|
||||
}
|
||||
};
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<PersistGate
|
||||
loading={null}
|
||||
persistor={persistor}
|
||||
onBeforeLift={onBeforeLift}
|
||||
>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
</PersistGate>
|
||||
</Provider>,
|
||||
|
|
|
@ -8,7 +8,6 @@ import configureStore from '../../../app/stores/windowStore';
|
|||
import { MonitorMessage } from '../../../app/middlewares/api';
|
||||
|
||||
import '../../views/window.pug';
|
||||
import { applyMediaFeaturesPreferences } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
const position = location.hash;
|
||||
|
||||
|
@ -26,17 +25,9 @@ chrome.runtime.getBackgroundPage((window) => {
|
|||
bg.onMessage.addListener(update);
|
||||
update();
|
||||
|
||||
const onBeforeLift = () => {
|
||||
localStore.dispatch(applyMediaFeaturesPreferences());
|
||||
};
|
||||
|
||||
render(
|
||||
<Provider store={localStore}>
|
||||
<PersistGate
|
||||
loading={null}
|
||||
persistor={persistor}
|
||||
onBeforeLift={onBeforeLift}
|
||||
>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
</PersistGate>
|
||||
</Provider>,
|
||||
|
|
|
@ -4,6 +4,20 @@ import { Provider } from 'react-redux';
|
|||
import configureStore from '../../../src/app/stores/windowStore';
|
||||
import App from '../../../src/app/containers/App';
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
const { store } = configureStore(store);
|
||||
|
||||
describe('App container', () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"lerna": "^4.0.0",
|
||||
"prettier": "2.4.1",
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.4.0",
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.4.0",
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
|
|
|
@ -3,6 +3,6 @@ module.exports = {
|
|||
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
|
||||
testEnvironment: 'jsdom',
|
||||
moduleNameMapper: {
|
||||
'\\.css$': '<rootDir>/test/__mocks__/styleMock.js',
|
||||
'\\.css$': '<rootDir>/test/__mocks__/styleMock.ts',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-loader": "^3.0.0",
|
||||
|
|
|
@ -3,7 +3,6 @@ import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket';
|
|||
import {
|
||||
CHANGE_SECTION,
|
||||
CHANGE_THEME,
|
||||
APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||
SELECT_INSTANCE,
|
||||
SELECT_MONITOR,
|
||||
UPDATE_MONITOR_STATE,
|
||||
|
@ -45,10 +44,9 @@ import {
|
|||
import { Action } from 'redux';
|
||||
import { Features, State } from '../reducers/instances';
|
||||
import { MonitorStateMonitorState } from '../reducers/monitor';
|
||||
import { LiftedAction, LiftedState } from '@redux-devtools/core';
|
||||
import { LiftedAction } from '@redux-devtools/core';
|
||||
import { Data } from '../reducers/reports';
|
||||
import { prefersDarkColorScheme } from '../utils/media-queries';
|
||||
import { ThemeColorPreference } from '../reducers/theme';
|
||||
import { LiftedState } from '@redux-devtools/core';
|
||||
|
||||
let monitorReducer: (
|
||||
monitorProps: unknown,
|
||||
|
@ -68,53 +66,19 @@ export function changeSection(section: string): ChangeSectionAction {
|
|||
interface ChangeThemeFormData {
|
||||
readonly theme: Theme;
|
||||
readonly scheme: Scheme;
|
||||
readonly themeColorPreference: ThemeColorPreference;
|
||||
readonly colorPreference: 'auto' | 'light' | 'dark';
|
||||
}
|
||||
export interface ChangeThemeData {
|
||||
interface ChangeThemeData {
|
||||
readonly formData: ChangeThemeFormData;
|
||||
}
|
||||
export interface ChangeThemeAction {
|
||||
readonly type: typeof CHANGE_THEME;
|
||||
readonly theme: Theme;
|
||||
readonly scheme: Scheme;
|
||||
readonly dark: boolean;
|
||||
readonly themeColorPreference: ThemeColorPreference;
|
||||
readonly colorPreference: 'auto' | 'light' | 'dark';
|
||||
}
|
||||
|
||||
export interface ApplyMediaFeaturesPreferencesAction {
|
||||
readonly type: typeof APPLY_MEDIA_FEATURES_PREFERENCES;
|
||||
readonly prefersDarkColorScheme: boolean;
|
||||
}
|
||||
|
||||
export function changeTheme(data: ChangeThemeData): ChangeThemeAction {
|
||||
const { themeColorPreference } = data.formData;
|
||||
let dark: boolean;
|
||||
|
||||
switch (themeColorPreference) {
|
||||
case 'light':
|
||||
dark = false;
|
||||
break;
|
||||
case 'dark':
|
||||
dark = true;
|
||||
break;
|
||||
default:
|
||||
dark = prefersDarkColorScheme();
|
||||
}
|
||||
|
||||
return { type: CHANGE_THEME, ...data.formData, dark };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#media_features
|
||||
*/
|
||||
export function applyMediaFeaturesPreferences(
|
||||
payload?: Partial<Omit<ApplyMediaFeaturesPreferencesAction, 'type'>>
|
||||
): ApplyMediaFeaturesPreferencesAction {
|
||||
return {
|
||||
prefersDarkColorScheme: prefersDarkColorScheme(),
|
||||
...payload,
|
||||
type: APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||
};
|
||||
return { type: CHANGE_THEME, ...data.formData };
|
||||
}
|
||||
|
||||
export interface InitMonitorAction {
|
||||
|
@ -600,7 +564,6 @@ export interface ErrorAction {
|
|||
export type StoreActionWithoutUpdateStateOrLiftedAction =
|
||||
| ChangeSectionAction
|
||||
| ChangeThemeAction
|
||||
| ApplyMediaFeaturesPreferencesAction
|
||||
| MonitorActionAction
|
||||
| SelectInstanceAction
|
||||
| SelectMonitorAction
|
||||
|
|
|
@ -4,10 +4,6 @@ import { Container, Form } from '@redux-devtools/ui';
|
|||
import { listSchemes, listThemes } from '@redux-devtools/ui/lib/utils/theme';
|
||||
import { changeTheme } from '../../actions';
|
||||
import { StoreState } from '../../reducers';
|
||||
import {
|
||||
defaultThemeColorPreference,
|
||||
themeColorPreferences,
|
||||
} from '../../reducers/theme';
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
||||
|
@ -19,8 +15,7 @@ export class Themes extends Component<Props> {
|
|||
const formData = {
|
||||
theme: theme.theme,
|
||||
scheme: theme.scheme,
|
||||
themeColorPreference:
|
||||
theme.themeColorPreference ?? defaultThemeColorPreference,
|
||||
colorPreference: theme.colorPreference,
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -38,10 +33,10 @@ export class Themes extends Component<Props> {
|
|||
type: 'string',
|
||||
enum: listSchemes(),
|
||||
},
|
||||
themeColorPreference: {
|
||||
colorPreference: {
|
||||
title: 'theme color',
|
||||
type: 'string',
|
||||
enum: themeColorPreferences as unknown as string[],
|
||||
enum: ['auto', 'light', 'dark'],
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
export const CHANGE_SECTION = 'main/CHANGE_SECTION';
|
||||
export const CHANGE_THEME = 'main/CHANGE_THEME';
|
||||
export const APPLY_MEDIA_FEATURES_PREFERENCES =
|
||||
'main/APPLY_MEDIA_FEATURES_PREFERENCES';
|
||||
|
||||
export const UPDATE_STATE = 'devTools/UPDATE_STATE';
|
||||
export const SET_STATE = 'devTools/SET_STATE';
|
||||
|
|
|
@ -7,7 +7,7 @@ import configureStore from './store/configureStore';
|
|||
import { CONNECT_REQUEST } from './constants/socketActionTypes';
|
||||
import App from './containers/App';
|
||||
import { StoreState } from './reducers';
|
||||
import { StoreAction, applyMediaFeaturesPreferences } from './actions';
|
||||
import { StoreAction } from './actions';
|
||||
|
||||
class Root extends Component {
|
||||
store?: Store<StoreState, StoreAction>;
|
||||
|
@ -27,26 +27,11 @@ class Root extends Component {
|
|||
this.persistor = persistor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* @private
|
||||
*/
|
||||
private _checkMediaFeaturesPreferences = () => {
|
||||
if (this.store) {
|
||||
this.store.dispatch(applyMediaFeaturesPreferences());
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.store) return null;
|
||||
|
||||
return (
|
||||
<Provider store={this.store}>
|
||||
<PersistGate
|
||||
loading={null}
|
||||
persistor={this.persistor!}
|
||||
onBeforeLift={this._checkMediaFeaturesPreferences}
|
||||
>
|
||||
<PersistGate loading={null} persistor={this.persistor!}>
|
||||
<App />
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
|
|
@ -1,33 +1,18 @@
|
|||
import { Theme, Scheme } from '@redux-devtools/ui';
|
||||
import {
|
||||
CHANGE_THEME,
|
||||
APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||
} from '../constants/actionTypes';
|
||||
import { Scheme, Theme } from '@redux-devtools/ui';
|
||||
import { CHANGE_THEME } from '../constants/actionTypes';
|
||||
import { StoreAction } from '../actions';
|
||||
|
||||
export const defaultThemeColorPreference = 'auto';
|
||||
|
||||
export const themeColorPreferences = [
|
||||
defaultThemeColorPreference,
|
||||
'light',
|
||||
'dark',
|
||||
] as const;
|
||||
|
||||
export type ThemeColorPreference = typeof themeColorPreferences[number];
|
||||
|
||||
export interface ThemeState {
|
||||
readonly theme: Theme;
|
||||
readonly scheme: Scheme;
|
||||
readonly light: boolean;
|
||||
readonly themeColorPreference?: ThemeColorPreference;
|
||||
readonly colorPreference: 'auto' | 'light' | 'dark';
|
||||
}
|
||||
|
||||
export default function theme(
|
||||
state: ThemeState = {
|
||||
theme: 'default' as const,
|
||||
scheme: 'default' as const,
|
||||
light: true,
|
||||
themeColorPreference: defaultThemeColorPreference,
|
||||
theme: 'default',
|
||||
scheme: 'default',
|
||||
colorPreference: 'auto',
|
||||
},
|
||||
action: StoreAction
|
||||
) {
|
||||
|
@ -35,23 +20,8 @@ export default function theme(
|
|||
return {
|
||||
theme: action.theme,
|
||||
scheme: action.scheme,
|
||||
light: !action.dark,
|
||||
themeColorPreference: action.themeColorPreference,
|
||||
colorPreference: action.colorPreference,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === APPLY_MEDIA_FEATURES_PREFERENCES &&
|
||||
(!state.themeColorPreference ||
|
||||
state.themeColorPreference === defaultThemeColorPreference)
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
themeColorPreference:
|
||||
state.themeColorPreference ?? defaultThemeColorPreference,
|
||||
light: !action.prefersDarkColorScheme,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
|
||||
*/
|
||||
export function prefersDarkColorScheme(): boolean {
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
typeof window.matchMedia === 'function'
|
||||
) {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
module.exports = {};
|
1
packages/redux-devtools-app/test/__mocks__/styleMock.ts
Normal file
1
packages/redux-devtools-app/test/__mocks__/styleMock.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export default {};
|
|
@ -9,6 +9,20 @@ import rootReducer from '../src/reducers';
|
|||
import { DATA_TYPE_KEY } from '../src/constants/dataTypes';
|
||||
import stringifyJSON from '../src/utils/stringifyJSON';
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
const store = createStore(rootReducer, applyMiddleware(exportState, api));
|
||||
|
||||
describe('App container', () => {
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"style-loader": "^3.3.1",
|
||||
|
|
|
@ -58,7 +58,11 @@ render(
|
|||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Container
|
||||
themeData={{ theme: 'default', scheme: 'default', light: true }}
|
||||
themeData={{
|
||||
theme: 'default',
|
||||
scheme: 'default',
|
||||
colorPreference: 'auto',
|
||||
}}
|
||||
>
|
||||
<Route path={ROOT}>
|
||||
<DemoApp />
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.4.0",
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
"@typescript-eslint/parser": "^5.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "~4.4.4"
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"style-loader": "^3.3.1",
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"style-loader": "^3.3.1",
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"react": "^17.0.2",
|
||||
"redux": "^4.1.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -13,6 +13,7 @@ export const globalTypes = {
|
|||
defaultValue: 'default',
|
||||
toolbar: {
|
||||
items: listThemes(),
|
||||
showName: true,
|
||||
},
|
||||
},
|
||||
scheme: {
|
||||
|
@ -21,6 +22,7 @@ export const globalTypes = {
|
|||
defaultValue: 'default',
|
||||
toolbar: {
|
||||
items: listSchemes(),
|
||||
showName: true,
|
||||
},
|
||||
},
|
||||
color: {
|
||||
|
@ -28,7 +30,8 @@ export const globalTypes = {
|
|||
description: 'Global color for components',
|
||||
defaultValue: 'light',
|
||||
toolbar: {
|
||||
items: ['light', 'dark'],
|
||||
items: ['auto', 'light', 'dark'],
|
||||
showName: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -38,7 +41,7 @@ const withThemeProvider = (Story, context) => (
|
|||
themeData={{
|
||||
theme: context.globals.theme,
|
||||
scheme: context.globals.scheme,
|
||||
light: context.globals.color === 'light',
|
||||
colorPreference: context.globals.color,
|
||||
}}
|
||||
>
|
||||
<Story {...context} />
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
|
@ -1,10 +1,28 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { getTheme, ThemeData } from '../utils/theme';
|
||||
import { useTheme, ThemeData } from '../utils/theme';
|
||||
import { MainContainerWrapper, ContainerWrapper } from './styles';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
interface ContainerFromThemeDataProps {
|
||||
children?: React.ReactNode;
|
||||
themeData: ThemeData;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ContainerFromThemeData: React.FunctionComponent<ContainerFromThemeDataProps> =
|
||||
({ themeData, className, children }) => {
|
||||
const theme = useTheme(themeData);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<MainContainerWrapper className={className}>
|
||||
{children}
|
||||
</MainContainerWrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
themeData?: ThemeData;
|
||||
|
@ -27,11 +45,9 @@ const Container: React.FunctionComponent<Props> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={getTheme(themeData)}>
|
||||
<MainContainerWrapper className={className}>
|
||||
{children}
|
||||
</MainContainerWrapper>
|
||||
</ThemeProvider>
|
||||
<ContainerFromThemeData themeData={themeData} className={className}>
|
||||
{children}
|
||||
</ContainerFromThemeData>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useEffect, useMemo, useState } from 'react';
|
||||
import * as themes from '../themes';
|
||||
import { nicinabox as defaultDarkScheme } from 'redux-devtools-themes';
|
||||
import * as baseSchemes from 'base16';
|
||||
|
@ -15,7 +16,7 @@ export type Scheme = keyof typeof schemes;
|
|||
export interface ThemeData {
|
||||
theme: keyof typeof themes;
|
||||
scheme: keyof typeof schemes;
|
||||
light: boolean;
|
||||
colorPreference: 'auto' | 'light' | 'dark';
|
||||
}
|
||||
|
||||
export interface ThemeFromProvider extends ThemeBase {
|
||||
|
@ -23,11 +24,11 @@ export interface ThemeFromProvider extends ThemeBase {
|
|||
light: boolean;
|
||||
}
|
||||
|
||||
export const getTheme = ({
|
||||
theme: type,
|
||||
scheme,
|
||||
light,
|
||||
}: ThemeData): ThemeFromProvider => {
|
||||
const getTheme = (
|
||||
type: keyof typeof themes,
|
||||
scheme: keyof typeof schemes,
|
||||
light: boolean
|
||||
): ThemeFromProvider => {
|
||||
let colors;
|
||||
if (scheme === 'default') {
|
||||
colors = light ? schemes.default : defaultDarkScheme;
|
||||
|
@ -47,3 +48,40 @@ export const getTheme = ({
|
|||
|
||||
return theme;
|
||||
};
|
||||
|
||||
export const useTheme = ({
|
||||
theme: type,
|
||||
scheme,
|
||||
colorPreference,
|
||||
}: ThemeData): ThemeFromProvider => {
|
||||
const [prefersDarkColorScheme, setPrefersDarkColorScheme] = useState(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
const handleChange = ({ matches }: MediaQueryListEvent) => {
|
||||
if (matches && !prefersDarkColorScheme) {
|
||||
setPrefersDarkColorScheme(true);
|
||||
}
|
||||
|
||||
if (!matches && prefersDarkColorScheme) {
|
||||
setPrefersDarkColorScheme(false);
|
||||
}
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handleChange);
|
||||
return () => mediaQuery.removeEventListener('change', handleChange);
|
||||
}, [prefersDarkColorScheme]);
|
||||
|
||||
const light = useMemo(
|
||||
() =>
|
||||
colorPreference === 'auto'
|
||||
? !prefersDarkColorScheme
|
||||
: colorPreference === 'light',
|
||||
[colorPreference, prefersDarkColorScheme]
|
||||
);
|
||||
|
||||
return getTheme(type, scheme, light);
|
||||
};
|
||||
|
|
|
@ -2,11 +2,29 @@ import React from 'react';
|
|||
import { render } from '@testing-library/react';
|
||||
import { Container } from '../src';
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
describe('Container', function () {
|
||||
it('renders correctly', () => {
|
||||
const { container } = render(
|
||||
<Container
|
||||
themeData={{ theme: 'default', scheme: 'default', light: false }}
|
||||
themeData={{
|
||||
theme: 'default',
|
||||
scheme: 'default',
|
||||
colorPreference: 'auto',
|
||||
}}
|
||||
>
|
||||
Text
|
||||
</Container>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
exports[`Container renders correctly 1`] = `
|
||||
<div
|
||||
class="sc-bdvvtL gyKeHC"
|
||||
class="sc-bdvvtL bKcxHw"
|
||||
>
|
||||
Text
|
||||
</div>
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.4.0",
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"style-loader": "^3.3.1",
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
23
yarn.lock
23
yarn.lock
|
@ -4576,6 +4576,7 @@ __metadata:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
file-loader: ^6.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-loader: ^3.0.0
|
||||
|
@ -4634,6 +4635,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
redux: ^4.1.2
|
||||
|
@ -4730,6 +4732,7 @@ __metadata:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
jest: ^27.3.1
|
||||
lodash: ^4.17.21
|
||||
prop-types: ^15.7.2
|
||||
|
@ -4765,6 +4768,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
parse-key: ^0.2.1
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
|
@ -4829,6 +4833,7 @@ __metadata:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
javascript-stringify: ^2.1.0
|
||||
jest: ^27.3.1
|
||||
jsan: ^3.1.13
|
||||
|
@ -4879,6 +4884,7 @@ __metadata:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
html-entities: ^2.3.2
|
||||
jest: ^27.3.1
|
||||
path-browserify: ^1.0.1
|
||||
|
@ -4927,6 +4933,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
hex-rgba: ^1.0.2
|
||||
immutable: ^4.0.0
|
||||
javascript-stringify: ^2.1.0
|
||||
|
@ -4998,6 +5005,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
lodash.debounce: ^4.0.8
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
|
@ -5030,7 +5038,6 @@ __metadata:
|
|||
"@typescript-eslint/parser": ^5.2.0
|
||||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
jsan: ^3.1.13
|
||||
querystring: ^0.2.1
|
||||
redux: ^4.1.2
|
||||
|
@ -5065,6 +5072,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
hex-rgba: ^1.0.2
|
||||
immutable: ^4.0.0
|
||||
jss: ^10.8.2
|
||||
|
@ -5132,6 +5140,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
redux: ^4.1.2
|
||||
|
@ -5182,6 +5191,7 @@ __metadata:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
jest: ^27.3.1
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
|
@ -11807,6 +11817,7 @@ __metadata:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
prop-types: ^15.7.2
|
||||
|
@ -16700,6 +16711,7 @@ fsevents@^1.2.7:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
history: ^4.10.1
|
||||
html-webpack-plugin: ^5.5.0
|
||||
|
@ -23744,6 +23756,7 @@ fsevents@^1.2.7:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
react: ^17.0.2
|
||||
|
@ -23782,6 +23795,7 @@ fsevents@^1.2.7:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
jest: ^27.3.1
|
||||
lodash.debounce: ^4.0.8
|
||||
prop-types: ^15.7.2
|
||||
|
@ -23947,6 +23961,7 @@ fsevents@^1.2.7:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
immutable: ^4.0.0
|
||||
|
@ -23984,6 +23999,7 @@ fsevents@^1.2.7:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
jest: ^27.3.1
|
||||
prop-types: ^15.7.2
|
||||
react: ^17.0.2
|
||||
|
@ -25352,6 +25368,7 @@ resolve@^2.0.0-next.3:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-jest: ^25.2.2
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
jest: ^27.3.1
|
||||
lerna: ^4.0.0
|
||||
prettier: 2.4.1
|
||||
|
@ -25400,6 +25417,7 @@ resolve@^2.0.0-next.3:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
framer-motion: ^4.1.17
|
||||
html-webpack-plugin: ^5.5.0
|
||||
|
@ -26171,6 +26189,7 @@ resolve@^2.0.0-next.3:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
prop-types: ^15.7.2
|
||||
|
@ -27670,6 +27689,7 @@ resolve@^2.0.0-next.3:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
history: ^4.10.1
|
||||
html-webpack-plugin: ^5.5.0
|
||||
|
@ -27949,6 +27969,7 @@ resolve@^2.0.0-next.3:
|
|||
eslint: ^7.32.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-react: ^7.26.1
|
||||
eslint-plugin-react-hooks: ^4.2.0
|
||||
fork-ts-checker-webpack-plugin: ^6.4.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
prop-types: ^15.7.2
|
||||
|
|
Loading…
Reference in New Issue
Block a user