diff --git a/README.md b/README.md index b7f7d9ee02e01f7ee3556072cc6dbed994dcbcd1..9c3807871ebe3b79db4788ce66a9c3a1e23c838b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ $ npm start ``` -Then navigate to [http://127.0.0.1:4444/__tests__/integration/mirador/](http://127.0.0.1:4444/__tests__/integration/mirador/) +Then navigate to [http://127.0.0.1:4444/\_\_tests\_\_/integration/mirador/](http://127.0.0.1:4444/\_\_tests\_\_/integration/mirador/) ### Instantiating Mirador diff --git a/__tests__/src/components/ManifestListItem.test.js b/__tests__/src/components/ManifestListItem.test.js index 2be1cd2218fd179df06542eefbea5ead915ca256..ee8d8891e4468c6f0d1d817739e90609bd5f1fc2 100644 --- a/__tests__/src/components/ManifestListItem.test.js +++ b/__tests__/src/components/ManifestListItem.test.js @@ -1,19 +1,23 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { store } from '../../../src/store'; -import ManifestListItem from '../../../src/components/ManifestListItem'; +import { ManifestListItem } from '../../../src/components/ManifestListItem'; describe('ManifestListItem', () => { it('renders without an error', () => { - const wrapper = shallow(<ManifestListItem manifest="http://example.com" store={store} />).dive(); + const addWindow = jest.fn(); + const wrapper = shallow( + <ManifestListItem manifest="http://example.com" addWindow={addWindow} />, + ); expect(wrapper.find('li.mirador-manifest-list-item').length).toBe(1); expect(wrapper.find('button').length).toBe(1); expect(wrapper.find('button').text()).toEqual('http://example.com'); }); it('updates and adds window when button clicked', () => { - const wrapper = shallow(<ManifestListItem manifest="http://example.com" store={store} />).dive(); - expect(store.getState().windows.length).toEqual(0); + const addWindow = jest.fn(); + const wrapper = shallow( + <ManifestListItem manifest="http://example.com" addWindow={addWindow} />, + ); wrapper.find('button').simulate('click'); - expect(store.getState().windows.length).toEqual(1); + expect(addWindow).toHaveBeenCalledTimes(1); }); }); diff --git a/__tests__/src/components/ViewerNavigation.test.js b/__tests__/src/components/ViewerNavigation.test.js index 407f5f33f7a0cb950b3b240037ef97ab0ee65f96..5e6408cddb3eb90ae2f10be6163828c1f13cb63b 100644 --- a/__tests__/src/components/ViewerNavigation.test.js +++ b/__tests__/src/components/ViewerNavigation.test.js @@ -1,6 +1,5 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { store } from '../../../src/store'; import { ViewerNavigation } from '../../../src/components/ViewerNavigation'; describe('ViewerNavigation', () => { diff --git a/__tests__/src/components/WindowTopBar.test.js b/__tests__/src/components/WindowTopBar.test.js index c03aeae9695da95d4bf727b3e77d1ba55ccc543f..0940b2ad113b3c9ca02b1adb8ebdddd4655293d3 100644 --- a/__tests__/src/components/WindowTopBar.test.js +++ b/__tests__/src/components/WindowTopBar.test.js @@ -1,28 +1,36 @@ import React from 'react'; -import { mount } from 'enzyme'; -import { actions, store } from '../../../src/store'; -import WindowTopBar from '../../../src/components/WindowTopBar'; -import fixture from '../../fixtures/24.json'; +import { shallow } from 'enzyme'; +import { WindowTopBar } from '../../../src/components/WindowTopBar'; -describe('Window', () => { - let wrapper; - let window; +const manifestFixture = { + manifestation: { + getLabel: () => [{ value: 'Fixture Label' }], + }, +}; + +describe('WindowTopBar', () => { + let topBar; + let mockRemoveWindow; beforeEach(() => { - store.dispatch(actions.receiveManifest('foo', fixture)); - store.dispatch(actions.addWindow({ manifestId: 'foo' })); - const manifest = store.getState().manifests.foo; - [window] = store.getState().windows; - wrapper = mount( - <WindowTopBar store={store} manifest={manifest} windowId={window.id} />, - // We need to attach this to something created by our JSDOM instance. - // Also need to provide context of the store so that connected sub components - // can render effectively. - { attachTo: document.getElementById('main'), context: { store } }, + mockRemoveWindow = jest.fn(); + topBar = shallow( + <WindowTopBar + manifest={manifestFixture} + windowId="foo" + removeWindow={mockRemoveWindow} + />, ); }); it('renders without an error', () => { - expect(wrapper.find('div.mirador-window-top-bar h3').text()).toBe('Test 24 Manifest: Image with IIIF Service - adapted with real image'); - expect(wrapper.find('button.mirador-window-close')); + expect(topBar.find('div.mirador-window-top-bar h3') + .text()).toBe('Fixture Label'); + expect(topBar.find('button.mirador-window-close')); + }); + + it('calls the removeWindow prop when the close button is clicked', () => { + topBar.find('button').simulate('click'); + expect(mockRemoveWindow).toHaveBeenCalledTimes(1); + expect(mockRemoveWindow).toHaveBeenCalledWith('foo'); }); }); diff --git a/__tests__/src/components/WorkspaceControlPanel.test.js b/__tests__/src/components/WorkspaceControlPanel.test.js new file mode 100644 index 0000000000000000000000000000000000000000..6e26b9fde4828d83f007cd3156d9f0bd7b531466 --- /dev/null +++ b/__tests__/src/components/WorkspaceControlPanel.test.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { actions, store } from '../../../src/store'; +import WorkspaceControlPanel from '../../../src/components/WorkspaceControlPanel'; +import fixture from '../../fixtures/2.json'; + +describe('WorkspaceControlPanel', () => { + let wrapper; + beforeEach(() => { + store.dispatch(actions.receiveManifest('foo', fixture)); + store.dispatch(actions.receiveManifest('bar', fixture)); + wrapper = shallow(<WorkspaceControlPanel store={store} />).dive(); + }); + + it('renders without an error', () => { + expect(wrapper.find('div.mirador-workspace-control-panel').length).toBe(1); + }); + + it('renders a list item for each manifest in the state', () => { + expect(wrapper.find('ul Connect(ManifestListItem)').length).toBe(2); + }); + + it('renders a Display component', () => { + expect(wrapper.find('Display').length).toBe(1); + }); +}); diff --git a/package.json b/package.json index 4891e9bf1f67580188d14a651fead86973f82558..d312076b2ce5991f1c1513fc2e7e8d144404b464 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "lint": "node_modules/.bin/eslint ./", + "lint": "node_modules/.bin/eslint ./ && node_modules/.bin/sass-lint -v ./src/styles/**/*", "server": "node_modules/.bin/http-server", "pretest": "npm run server:json", "posttest": "node ./scripts/json-server/kill-server.js", @@ -24,7 +24,7 @@ "repository": "https://github.com/ProjectMirador/mirador", "dependencies": { "css-ns": "^1.2.2", - "deepmerge": "^3.0.0", + "deepmerge": "^3.1.0", "manifesto.js": "^3.0.9", "node-fetch": "2.1.1", "node-sass": "^4.9.2", @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.14.0", "eslint-plugin-jest": "^22.1.2", "eslint-plugin-jsx-a11y": "^6.1.2", - "eslint-plugin-react": "^7.11.1", + "eslint-plugin-react": "^7.12.4", "http-server": "^0.11.1", "jest": "^22.4.4", "jest-fetch-mock": "^1.5.0", @@ -69,6 +69,7 @@ "puppeteer": "^1.4.0", "react-dev-utils": "^6.1.1", "redux-mock-store": "^1.5.1", + "sass-lint": "^1.12.1", "style-loader": "^0.22.1", "terser-webpack-plugin": "^1.2.1", "webpack": "^4.27.1", diff --git a/src/components/App.js b/src/components/App.js index 1c9743ca35587e370f9a13301d37639b229e2d9f..735d989aeebb2ef9e5abbed7dff9827a44c3078e 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,10 +1,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import { actions } from '../store'; -import Display from './Display'; -import ManifestForm from './ManifestForm'; -import ManifestListItem from './ManifestListItem'; +import WorkspaceControlPanel from './WorkspaceControlPanel'; import Workspace from './Workspace'; import ns from '../config/css-ns'; @@ -12,85 +9,21 @@ import ns from '../config/css-ns'; * This is the top level Mirador component. * @prop {Object} manifests */ -class App extends Component { - /** - * constructor - - */ - constructor(props) { - super(props); - this.state = { - lastRequested: '', - }; - - this.setLastRequested = this.setLastRequested.bind(this); - } - - /** - * setLastRequested - Sets the state lastRequested - * - * @private - */ - setLastRequested(requested) { - this.setState({ - lastRequested: requested, - }); - } - - /** - * computedContent - computes the content to be displayed based on logic - * - * @return {type} description - * @private - */ - computedContent() { - const { manifests } = this.props; - const { lastRequested } = this.state; - const manifest = manifests[lastRequested]; - if (manifest) { - if (manifest.isFetching) { - return '☕'; - } - if (manifest.error) { - return manifest.error.message; - } - return JSON.stringify(manifest.json, 0, 2); - } - return 'Nothing Selected Yet'; - } - +export class App extends Component { /** * render * @return {String} - HTML markup for the component */ render() { - const { manifests } = this.props; - const { lastRequested } = this.state; - const manifestList = Object.keys(manifests).map(manifest => ( - <ManifestListItem - key={manifest} - manifest={manifest} - /> - )); return ( <div className={ns('app')}> <Workspace /> - <div className={ns('control-panel')}> - <ManifestForm setLastRequested={this.setLastRequested} /> - <ul>{manifestList}</ul> - - <Display - manifest={manifests[lastRequested]} - /> - </div> + <WorkspaceControlPanel /> </div> ); } } -App.propTypes = { - manifests: PropTypes.instanceOf(Object).isRequired, -}; - /** * mapStateToProps - to hook up connect * @memberof App @@ -107,11 +40,7 @@ const mapStateToProps = state => ( * @memberof App * @private */ -const mapDispatchToProps = dispatch => ({ - fetchManifest: manifestUrl => ( - dispatch(actions.fetchManifest(manifestUrl)) - ), -}); +const mapDispatchToProps = { fetchManifest: actions.fetchManifest }; export default connect( mapStateToProps, diff --git a/src/components/ManifestForm.js b/src/components/ManifestForm.js index 54f1557e96ec08d11bc9792c6c526d7cdd132d69..380027ee24961c0005ae77553244814de6b7cec5 100644 --- a/src/components/ManifestForm.js +++ b/src/components/ManifestForm.js @@ -8,7 +8,7 @@ import { actions } from '../store'; * @prop {Function} fetchManifest * @prop {Function} setLastRequested */ -class ManifestForm extends Component { +export class ManifestForm extends Component { /** * constructor - */ @@ -87,11 +87,7 @@ const mapStateToProps = () => ( * @memberof ManifestForm * @private */ -const mapDispatchToProps = dispatch => ({ - fetchManifest: manifestUrl => ( - dispatch(actions.fetchManifest(manifestUrl)) - ), -}); +const mapDispatchToProps = { fetchManifest: actions.fetchManifest }; export default connect( mapStateToProps, diff --git a/src/components/ManifestListItem.js b/src/components/ManifestListItem.js index b67886e8d869abb540943b088ef0246b6dcef84e..0c47f9bce9a56d6a0c1ee622c01e416eb91a67fc 100644 --- a/src/components/ManifestListItem.js +++ b/src/components/ManifestListItem.js @@ -23,7 +23,7 @@ const handleOpenButtonClick = (event, manifest, addWindow) => { * @memberof ManifestListItem * @private */ -const ManifestListItem = ({ manifest, addWindow }) => ( +export const ManifestListItem = ({ manifest, addWindow }) => ( <li className={ns('manifest-list-item')}> <button type="button" onClick={event => handleOpenButtonClick(event, manifest, addWindow)}> {manifest} @@ -50,11 +50,7 @@ const mapStateToProps = () => ( * @memberof ManifestListItem * @private */ -const mapDispatchToProps = dispatch => ({ - addWindow: options => ( - dispatch(actions.addWindow(options)) - ), -}); +const mapDispatchToProps = { addWindow: actions.addWindow }; export default connect( mapStateToProps, diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index a707af25b8572abbb3e997e33b79bb7eb567d1eb..7a528034c84c4a18723e9b6a2d240095d9e72703 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import OpenSeadragon from 'openseadragon'; import miradorWithPlugins from '../lib/miradorWithPlugins'; @@ -8,7 +8,7 @@ import ns from '../config/css-ns'; * Represents a OpenSeadragonViewer in the mirador workspace. Responsible for mounting * and rendering OSD. */ -class OpenSeadragonViewer extends Component { +export class OpenSeadragonViewer extends Component { /** * @param {Object} props */ @@ -70,13 +70,13 @@ class OpenSeadragonViewer extends Component { render() { const { window } = this.props; return ( - <Fragment> + <> <div className={ns('osd-container')} id={`${window.id}-osd`} ref={this.ref} /> - </Fragment> + </> ); } } diff --git a/src/components/ViewerNavigation.js b/src/components/ViewerNavigation.js index a84b4895427249039baa88aa8c9a9e1779f18fc9..11b54337639169dfd9baa546bb14233bbd27053b 100644 --- a/src/components/ViewerNavigation.js +++ b/src/components/ViewerNavigation.js @@ -85,9 +85,9 @@ ViewerNavigation.propTypes = { * @memberof ManifestForm * @private */ -const mapDispatchToProps = dispatch => ({ - nextCanvas: windowId => dispatch(actions.nextCanvas(windowId)), - previousCanvas: windowId => dispatch(actions.previousCanvas(windowId)), -}); +const mapDispatchToProps = { + nextCanvas: actions.nextCanvas, + previousCanvas: actions.previousCanvas, +}; export default connect(null, mapDispatchToProps)(miradorWithPlugins(ViewerNavigation)); diff --git a/src/components/Window.js b/src/components/Window.js index c524dded55ffd1792a49a70ad0a9b465b85dada0..7b181c480d455a2202fd217997a24db58f054f59 100644 --- a/src/components/Window.js +++ b/src/components/Window.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ns from '../config/css-ns'; import WindowBackground from './WindowBackground'; -import WindowTopBar from './WindowTopBar'; +import ConnectedWindowTopBar from './WindowTopBar'; import WindowViewer from './WindowViewer'; /** @@ -26,7 +26,7 @@ class Window extends Component { */ renderViewer() { const { manifest, window } = this.props; - if (manifest) { + if (manifest && manifest.isFetching === false) { return ( <WindowViewer window={window} @@ -44,7 +44,7 @@ class Window extends Component { const { manifest, window } = this.props; return ( <div className={ns('window')} style={this.styleAttributes()}> - <WindowTopBar + <ConnectedWindowTopBar windowId={window.id} manifest={manifest} /> diff --git a/src/components/WindowTopBar.js b/src/components/WindowTopBar.js index 19b7e35664508b44497f789a65e4c051f9df3c55..3bab8cd5afab2eda1a35293f16a605c272883b8f 100644 --- a/src/components/WindowTopBar.js +++ b/src/components/WindowTopBar.js @@ -2,14 +2,14 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { actions } from '../store'; -import WindowTopBarButtons from './WindowTopBarButtons'; +import ConnectedWindowTopBarButtons from './WindowTopBarButtons'; import miradorWithPlugins from '../lib/miradorWithPlugins'; import ns from '../config/css-ns'; /** * WindowTopBar */ -class WindowTopBar extends Component { +export class WindowTopBar extends Component { /** * titleContent * @@ -32,7 +32,7 @@ class WindowTopBar extends Component { return ( <div className={ns('window-top-bar')}> <h3>{this.titleContent()}</h3> - <WindowTopBarButtons windowId={windowId} /> + <ConnectedWindowTopBarButtons windowId={windowId} /> <button type="button" className={ns('window-close')} aria-label="Close Window" onClick={() => removeWindow(windowId)}>×</button> </div> ); @@ -44,11 +44,7 @@ class WindowTopBar extends Component { * @memberof ManifestListItem * @private */ -const mapDispatchToProps = dispatch => ({ - removeWindow: windowId => ( - dispatch(actions.removeWindow(windowId)) - ), -}); +const mapDispatchToProps = { removeWindow: actions.removeWindow }; WindowTopBar.propTypes = { manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types diff --git a/src/components/WindowTopBarButtons.js b/src/components/WindowTopBarButtons.js index 6bcdef6b45cd95ca78df88b7dd221d597ef62b65..b6f056d38e8d15e0fad34a120ed60ba50d7e2e47 100644 --- a/src/components/WindowTopBarButtons.js +++ b/src/components/WindowTopBarButtons.js @@ -1,18 +1,17 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import miradorWithPlugins from '../lib/miradorWithPlugins'; + /** * */ -class WindowTopBarButtons extends Component { +export class WindowTopBarButtons extends Component { /** * render * * @return {type} description */ render() { - return ( - <Fragment /> - ); + return (<></>); } } diff --git a/src/components/WindowViewer.js b/src/components/WindowViewer.js index 1e157eeca7eb5e6d6fed6ad4d055ccf43dd164e4..4d2ea86fd1a7d28b4119a1b8861a5c8272940e6f 100644 --- a/src/components/WindowViewer.js +++ b/src/components/WindowViewer.js @@ -1,9 +1,9 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { actions } from '../store'; import miradorWithPlugins from '../lib/miradorWithPlugins'; -import OpenSeadragonViewer from './OpenSeadragonViewer'; +import ConnectedOSDViewer from './OpenSeadragonViewer'; import ConnectedViewerNavigation from './ViewerNavigation'; /** @@ -80,13 +80,13 @@ class WindowViewer extends Component { render() { const { window } = this.props; return ( - <Fragment> - <OpenSeadragonViewer + <> + <ConnectedOSDViewer tileSources={this.tileInfoFetchedFromStore()} window={window} /> <ConnectedViewerNavigation window={window} canvases={this.canvases} /> - </Fragment> + </> ); } } @@ -107,9 +107,7 @@ const mapStateToProps = state => ( * @memberof WindowViewer * @private */ -const mapDispatchToProps = dispatch => ({ - fetchInfoResponse: infoId => dispatch(actions.fetchInfoResponse(infoId)), -}); +const mapDispatchToProps = { fetchInfoResponse: actions.fetchInfoResponse }; WindowViewer.propTypes = { infoResponses: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types diff --git a/src/components/WorkspaceControlPanel.js b/src/components/WorkspaceControlPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..da97568bc9b22a7dd019f4a9b1c7d8ade313cef5 --- /dev/null +++ b/src/components/WorkspaceControlPanel.js @@ -0,0 +1,77 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import Display from './Display'; +import ConnectedManifestForm from './ManifestForm'; +import ConnectedManifestListItem from './ManifestListItem'; +import ns from '../config/css-ns'; + +/** + * Provides the panel responsible for controlling the entire workspace + */ +class WorkspaceControlPanel extends Component { + /** + * constructor - + */ + constructor(props) { + super(props); + this.state = { + lastRequested: '', + }; + + this.setLastRequested = this.setLastRequested.bind(this); + } + + /** + * setLastRequested - Sets the state lastRequested + * + * @private + */ + setLastRequested(requested) { + this.setState({ + lastRequested: requested, + }); + } + + /** + * render + * @return {String} - HTML markup for the component + */ + render() { + const { manifests } = this.props; + const { lastRequested } = this.state; + const manifestList = Object.keys(manifests).map(manifest => ( + <ConnectedManifestListItem + key={manifest} + manifest={manifest} + /> + )); + return ( + <div className={ns('workspace-control-panel')}> + <ConnectedManifestForm setLastRequested={this.setLastRequested} /> + <ul>{manifestList}</ul> + + <Display + manifest={manifests[lastRequested]} + /> + </div> + ); + } +} + +WorkspaceControlPanel.propTypes = { + manifests: PropTypes.instanceOf(Object).isRequired, +}; + +/** + * mapStateToProps - to hook up connect + * @memberof WorkspaceControlPanel + * @private + */ +const mapStateToProps = state => ( + { + manifests: state.manifests, + } +); + +export default connect(mapStateToProps)(WorkspaceControlPanel); diff --git a/src/init.js b/src/init.js index 0744dd999cdd5cec9a1d09bd57e63deea5b6a06d..fb42874b70d44249213c6f12f176b883305e5cd2 100644 --- a/src/init.js +++ b/src/init.js @@ -2,7 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import deepmerge from 'deepmerge'; -import App from './components/App'; +import ConnectedApp from './components/App'; import createRootReducer from './reducers/index'; import { store, actions } from './store'; import settings from './config/settings'; @@ -57,7 +57,7 @@ export default function (config) { ReactDOM.render( <Provider store={store}> - <App config={config} /> + <ConnectedApp config={config} /> </Provider>, document.getElementById(config.id), ); diff --git a/src/lib/miradorWithPlugins.js b/src/lib/miradorWithPlugins.js index 16a9619ffcb0ae5986ee3193ab56553bfd4a17b1..8bb3a22618b52ce70a6e2a678c20ec054e6e71da 100644 --- a/src/lib/miradorWithPlugins.js +++ b/src/lib/miradorWithPlugins.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import componentPlugins from './componentPlugins'; @@ -34,7 +34,7 @@ export default function miradorWithPlugins(WrappedComponent) { const { config } = this.props; const { plugins } = config; return ( - <Fragment> + <> <WrappedComponent {...this.props} ref={(parent) => { this.pluginParent = parent; }} /> { /* TODO: Refactor .name here in some way so we dont need to rely on it */} {componentPlugins(WrappedComponent.name, plugins) @@ -43,7 +43,7 @@ export default function miradorWithPlugins(WrappedComponent) { { key: component.name, ...this.props, pluginParent: this.getPluginParent }, )) } - </Fragment> + </> ); } } diff --git a/src/styles/index.scss b/src/styles/index.scss index f37eb5dc2f4a4d823401ae7b2cf93931f407bbd7..5f05992a46c70a908ad9f6a2f79b0079b54d9754 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,42 +1,44 @@ +@import 'variables'; + body { - background: white; + background: $white; height: 100%; } .mirador { - &-control-panel { - position: absolute; + &-workspace-control-panel { + border-right: 2px solid $black; + bottom: 0; box-sizing: border-box; + left: 0; + margin: 0; padding: 15px; - margin:0px; - left:0; - top:0; - bottom:0; + position: absolute; + top: 0; width: 300px; - border-right: 2px solid black; } &-workspace { - position: absolute; + bottom: 0; box-sizing: border-box; + left: 0; margin: 0; - padding-left: 300px; - top:0; - right:0; - bottom:0; - left:0; overflow: scroll; + padding-left: 300px; + position: absolute; + right: 0; + top: 0; } &-window { - border: 1px solid black; + border: 1px solid $black; overflow: hidden; position: relative; } &-window-top-bar { - background: linear-gradient(to bottom, rgba(0, 0, 0, .65) 0%, rgba(0, 0, 0, 0) 100%); - color: white; + background: linear-gradient(to bottom, $window-top-bar-gradient-top 0%, $window-top-bar-gradient-bottom 100%); + color: $white; position: absolute; z-index: 10; @@ -46,7 +48,7 @@ body { } &-osd-container { - background: gray; + background: $gray; bottom: 0; height: 100%; position: absolute; diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000000000000000000000000000000000000..a680a465819ad2d6bbfe498d8a8cb137837ba11f --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,5 @@ +$black: #000; +$gray: #808080; +$white: #fff; +$window-top-bar-gradient-top: rgba(0, 0, 0, .65); +$window-top-bar-gradient-bottom: rgba(0, 0, 0, 0);