feat: color theming for monitors based on block category
This commit is contained in:
1 parent
bb618d09cb
commit
563dffb34a
11 files changed
+123
-30
No files matched your search
@@ -10,7 +10,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => (
|
||||
</div>
|
||||
<div
|
||||
className={styles.value}
|
||||
style={{background: categoryColor}}
|
||||
style={{
|
||||
background: categoryColor.background,
|
||||
color: categoryColor.text
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
@@ -19,7 +22,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => (
|
||||
);
|
||||
|
||||
DefaultMonitor.propTypes = {
|
||||
categoryColor: PropTypes.string.isRequired,
|
||||
categoryColor: PropTypes.shape({
|
||||
background: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
|
||||
@@ -6,7 +6,10 @@ const LargeMonitor = ({categoryColor, value}) => (
|
||||
<div className={styles.largeMonitor}>
|
||||
<div
|
||||
className={styles.largeValue}
|
||||
style={{background: categoryColor}}
|
||||
style={{
|
||||
background: categoryColor.background,
|
||||
color: categoryColor.text
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
@@ -14,7 +17,10 @@ const LargeMonitor = ({categoryColor, value}) => (
|
||||
);
|
||||
|
||||
LargeMonitor.propTypes = {
|
||||
categoryColor: PropTypes.string,
|
||||
categoryColor: PropTypes.shape({
|
||||
background: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
|
||||
@@ -41,7 +41,10 @@ class ListMonitorScroller extends React.Component {
|
||||
<div
|
||||
className={styles.listValue}
|
||||
dataIndex={index}
|
||||
style={{background: this.props.categoryColor}}
|
||||
style={{
|
||||
background: this.props.categoryColor.background,
|
||||
color: this.props.categoryColor.text
|
||||
}}
|
||||
onClick={this.props.draggable ? this.handleEventFactory(index) : null}
|
||||
>
|
||||
{this.props.draggable && this.props.activeIndex === index ? (
|
||||
@@ -51,6 +54,7 @@ class ListMonitorScroller extends React.Component {
|
||||
autoComplete={false}
|
||||
className={classNames(styles.listInput, 'no-drag')}
|
||||
spellCheck={false}
|
||||
style={{color: this.props.categoryColor.text}}
|
||||
type="text"
|
||||
value={this.props.activeValue}
|
||||
onBlur={this.props.onDeactivate}
|
||||
@@ -97,7 +101,10 @@ class ListMonitorScroller extends React.Component {
|
||||
ListMonitorScroller.propTypes = {
|
||||
activeIndex: PropTypes.number,
|
||||
activeValue: PropTypes.string,
|
||||
categoryColor: PropTypes.string,
|
||||
categoryColor: PropTypes.shape({
|
||||
background: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
draggable: PropTypes.bool,
|
||||
height: PropTypes.number,
|
||||
onActivate: PropTypes.func,
|
||||
|
||||
@@ -54,7 +54,10 @@ const ListMonitor = ({draggable, label, width, height, value, onResizeMouseDown,
|
||||
|
||||
ListMonitor.propTypes = {
|
||||
activeIndex: PropTypes.number,
|
||||
categoryColor: PropTypes.string.isRequired,
|
||||
categoryColor: PropTypes.shape({
|
||||
background: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
draggable: PropTypes.bool.isRequired,
|
||||
height: PropTypes.number,
|
||||
label: PropTypes.string.isRequired,
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
align-items: center;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin: 0 5px;
|
||||
border-radius: calc($space / 2);
|
||||
padding: 0 2px;
|
||||
@@ -55,7 +54,6 @@
|
||||
min-width: 3rem;
|
||||
padding: 0.1rem 0.25rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
white-space: pre-wrap;
|
||||
transform: translateZ(0); /* Fixes flickering in Safari */
|
||||
@@ -115,7 +113,6 @@
|
||||
.list-value {
|
||||
min-width: 40px;
|
||||
text-align: left;
|
||||
color: white;
|
||||
margin: 0 3px;
|
||||
border-radius: calc($space / 2);
|
||||
border: 1px solid $ui-black-transparent;
|
||||
@@ -161,7 +158,6 @@
|
||||
padding: 3px 5px;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: $ui-white;
|
||||
outline: none;
|
||||
font-size: 0.75rem;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@@ -174,7 +170,6 @@
|
||||
padding: 0;
|
||||
padding-right: 5px;
|
||||
cursor: pointer;
|
||||
color: $ui-white;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
|
||||
@@ -10,17 +10,19 @@ import DefaultMonitor from './default-monitor.jsx';
|
||||
import LargeMonitor from './large-monitor.jsx';
|
||||
import SliderMonitor from '../../containers/slider-monitor.jsx';
|
||||
import ListMonitor from '../../containers/list-monitor.jsx';
|
||||
import {getColorsForTheme} from '../../lib/themes/index.js';
|
||||
|
||||
import styles from './monitor.css';
|
||||
|
||||
const categories = {
|
||||
data: '#FF8C1A',
|
||||
sensing: '#5CB1D6',
|
||||
sound: '#CF63CF',
|
||||
looks: '#9966FF',
|
||||
motion: '#4C97FF',
|
||||
list: '#FC662C',
|
||||
extension: '#0FBD8C'
|
||||
// Map category name to color name used in scratch-blocks Blockly.Colours
|
||||
const categoryColorMap = {
|
||||
data: 'data',
|
||||
sensing: 'sensing',
|
||||
sound: 'sounds',
|
||||
looks: 'looks',
|
||||
motion: 'motion',
|
||||
list: 'data_lists',
|
||||
extension: 'pen'
|
||||
};
|
||||
|
||||
const modes = {
|
||||
@@ -30,6 +32,14 @@ const modes = {
|
||||
list: ListMonitor
|
||||
};
|
||||
|
||||
const getCategoryColor = (theme, category) => {
|
||||
const colors = getColorsForTheme(theme);
|
||||
return {
|
||||
background: colors[categoryColorMap[category]].primary,
|
||||
text: colors.text
|
||||
};
|
||||
};
|
||||
|
||||
const MonitorComponent = props => (
|
||||
<ContextMenuTrigger
|
||||
disable={!props.draggable}
|
||||
@@ -49,7 +59,7 @@ const MonitorComponent = props => (
|
||||
onDoubleClick={props.mode === 'list' || !props.draggable ? null : props.onNextMode}
|
||||
>
|
||||
{React.createElement(modes[props.mode], {
|
||||
categoryColor: categories[props.category],
|
||||
categoryColor: getCategoryColor(props.theme, props.category),
|
||||
...props
|
||||
})}
|
||||
</Box>
|
||||
@@ -122,12 +132,10 @@ const MonitorComponent = props => (
|
||||
|
||||
);
|
||||
|
||||
MonitorComponent.categories = categories;
|
||||
|
||||
const monitorModes = Object.keys(modes);
|
||||
|
||||
MonitorComponent.propTypes = {
|
||||
category: PropTypes.oneOf(Object.keys(categories)),
|
||||
category: PropTypes.oneOf(Object.keys(categoryColorMap)),
|
||||
componentRef: PropTypes.func.isRequired,
|
||||
draggable: PropTypes.bool.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
@@ -140,7 +148,8 @@ MonitorComponent.propTypes = {
|
||||
onSetModeToDefault: PropTypes.func,
|
||||
onSetModeToLarge: PropTypes.func,
|
||||
onSetModeToSlider: PropTypes.func,
|
||||
onSliderPromptOpen: PropTypes.func
|
||||
onSliderPromptOpen: PropTypes.func,
|
||||
theme: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
MonitorComponent.defaultProps = {
|
||||
|
||||
@@ -12,7 +12,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli
|
||||
</div>
|
||||
<div
|
||||
className={styles.value}
|
||||
style={{background: categoryColor}}
|
||||
style={{
|
||||
background: categoryColor.background,
|
||||
color: categoryColor.text
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
@@ -33,7 +36,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli
|
||||
);
|
||||
|
||||
SliderMonitor.propTypes = {
|
||||
categoryColor: PropTypes.string.isRequired,
|
||||
categoryColor: PropTypes.shape({
|
||||
background: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
isDiscrete: PropTypes.bool,
|
||||
label: PropTypes.string.isRequired,
|
||||
max: PropTypes.number,
|
||||
|
||||
@@ -213,6 +213,7 @@ class Monitor extends React.Component {
|
||||
min={this.props.min}
|
||||
mode={this.props.mode}
|
||||
targetId={this.props.targetId}
|
||||
theme={this.props.theme}
|
||||
width={this.props.width}
|
||||
onDragEnd={this.handleDragEnd}
|
||||
onExport={isList ? this.handleExport : null}
|
||||
@@ -250,6 +251,7 @@ Monitor.propTypes = {
|
||||
resizeMonitorRect: PropTypes.func.isRequired,
|
||||
spriteName: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
||||
targetId: PropTypes.string,
|
||||
theme: PropTypes.string,
|
||||
toolboxXML: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
@@ -264,8 +266,12 @@ Monitor.propTypes = {
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number
|
||||
};
|
||||
Monitor.defaultProps = {
|
||||
theme: 'standard'
|
||||
};
|
||||
const mapStateToProps = state => ({
|
||||
monitorLayout: state.scratchGui.monitorLayout,
|
||||
theme: state.scratchGui.theme.theme,
|
||||
// render on toolbox updates since changes to the blocks could affect monitor labels, i.e. updated locale
|
||||
toolboxXML: state.scratchGui.toolbox.toolboxXML,
|
||||
vm: state.scratchGui.vm
|
||||
|
||||
@@ -70,9 +70,6 @@ const blockColors = {
|
||||
tertiary: '#FF3355',
|
||||
quaternary: '#FFE5EA'
|
||||
},
|
||||
// For productionizing: we should look into making this field be the color of the text on the blocks
|
||||
// e.g. move and steps in move (10) steps, and rename this property below for controlling the text inside
|
||||
// of inputs e.g. 10
|
||||
text: '#000000',
|
||||
textFieldText: '#000000', // Text inside of inputs e.g. 90 in [point in direction (90)]
|
||||
toolboxText: '#000000', // Toolbox text, color picker text (used to be #575E75)
|
||||
|
||||
@@ -11,6 +11,9 @@ describe('MonitorListComponent', () => {
|
||||
monitors: {},
|
||||
savedMonitorPositions: {}
|
||||
},
|
||||
theme: {
|
||||
theme: 'standard'
|
||||
},
|
||||
toolbox: {
|
||||
toolboxXML: ''
|
||||
},
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import DefaultMonitor from '../../../src/components/monitor/default-monitor';
|
||||
import Monitor from '../../../src/components/monitor/monitor';
|
||||
|
||||
jest.mock('../../../src/lib/themes/default-colors');
|
||||
jest.mock('../../../src/lib/themes/dark-mode');
|
||||
|
||||
describe('Monitor Component', () => {
|
||||
test('it selects the correct colors based on default theme', () => {
|
||||
const noop = () => {};
|
||||
|
||||
const wrapper = shallow(<Monitor
|
||||
category="motion"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
componentRef={noop}
|
||||
draggable={false}
|
||||
label="My label"
|
||||
mode="default"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onDragEnd={noop}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onNextMode={noop}
|
||||
theme="standard"
|
||||
/>);
|
||||
|
||||
const defaultMonitor = wrapper.find(DefaultMonitor);
|
||||
|
||||
// selects colors from mock value in src/lib/themes/__mocks__/default-colors.js
|
||||
expect(defaultMonitor.props().categoryColor).toEqual({background: '#111111', text: '#444444'});
|
||||
});
|
||||
|
||||
test('it selects the correct colors based on dark mode theme', () => {
|
||||
const noop = () => {};
|
||||
|
||||
const wrapper = shallow(<Monitor
|
||||
category="motion"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
componentRef={noop}
|
||||
draggable={false}
|
||||
label="My label"
|
||||
mode="default"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onDragEnd={noop}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onNextMode={noop}
|
||||
theme="dark-mode"
|
||||
/>);
|
||||
|
||||
const defaultMonitor = wrapper.find(DefaultMonitor);
|
||||
|
||||
// selects colors from mock value in src/lib/themes/__mocks__/dark-mode.js
|
||||
expect(defaultMonitor.props().categoryColor).toEqual({background: '#AAAAAA', text: '#BBBBBB'});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user