diff options
author | Tobias Urdin <tobias.urdin@binero.se> | 2023-03-15 23:36:45 +0000 |
---|---|---|
committer | Tobias Urdin <tobias.urdin@binero.se> | 2023-04-21 11:23:56 +0000 |
commit | 59cd5de78baa31150958e6d0d6733407c0e95805 (patch) | |
tree | ed72bd938cc5d40a04de612e30547a78ea9c53cf /web/src/pages | |
parent | de9dfa2bc416d9b1bb6159ed39a014fbba267db5 (diff) | |
download | zuul-59cd5de78baa31150958e6d0d6733407c0e95805.tar.gz |
web: add dark mode and theme selection
This adds a theme selection in the preferences in the
config modal and adds a new dark theme.
Removes the line.png image and instead uses CSS
linear-gradient that is available in all browsers
since around 2018, also fixes the 15 pixels spacing
issue that is there today.
You can select between three different themes.
Auto will use your system preference to choose either the
light or dark theme, changes dynamically based on your
system preference.
Light is the current theme.
Dark is the theme added by this patch series.
The UX this changes is that if somebody has their system
preferences set to dark, for example in Mac OS X that is
in System Settings -> Appearance -> Dark the user will
get the Zuul web UI in dark by default and same for the
opposite.
This uses a poor man's dark mode for swagger-ui
as per the comment in [1].
[1] https://github.com/swagger-api/swagger-ui/issues/5327#issuecomment-742375520
Change-Id: I01cf32f3decdb885307a76eb79d644667bbbf9a3
Diffstat (limited to 'web/src/pages')
-rw-r--r-- | web/src/pages/Autohold.jsx | 10 | ||||
-rw-r--r-- | web/src/pages/Build.jsx | 8 | ||||
-rw-r--r-- | web/src/pages/Buildset.jsx | 6 | ||||
-rw-r--r-- | web/src/pages/Buildsets.jsx | 8 | ||||
-rw-r--r-- | web/src/pages/ConfigErrors.jsx | 23 | ||||
-rw-r--r-- | web/src/pages/FreezeJob.jsx | 5 | ||||
-rw-r--r-- | web/src/pages/Job.jsx | 6 | ||||
-rw-r--r-- | web/src/pages/Jobs.jsx | 8 | ||||
-rw-r--r-- | web/src/pages/OpenApi.jsx | 6 | ||||
-rw-r--r-- | web/src/pages/Project.jsx | 4 | ||||
-rw-r--r-- | web/src/pages/Semaphore.jsx | 6 | ||||
-rw-r--r-- | web/src/pages/Status.jsx | 3 | ||||
-rw-r--r-- | web/src/pages/Stream.jsx | 11 |
13 files changed, 71 insertions, 33 deletions
diff --git a/web/src/pages/Autohold.jsx b/web/src/pages/Autohold.jsx index 0d0198b45..cc91cbcd0 100644 --- a/web/src/pages/Autohold.jsx +++ b/web/src/pages/Autohold.jsx @@ -59,6 +59,7 @@ class AutoholdPage extends React.Component { autohold: PropTypes.object, isFetching: PropTypes.bool.isRequired, fetchAutohold: PropTypes.func.isRequired, + preferences: PropTypes.object, } updateData = () => { @@ -147,7 +148,7 @@ class AutoholdPage extends React.Component { return ( <> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Title headingLevel="h2">Autohold Request {autohold.id}</Title> <Flex className="zuul-autohold-attributes"> @@ -211,7 +212,9 @@ class AutoholdPage extends React.Component { value={ <> <strong>Reason:</strong> - <pre>{autohold.reason}</pre> + <div className={this.props.preferences.darkMode ? 'zuul-console-dark' : ''}> + <pre>{autohold.reason}</pre> + </div> </> } /> @@ -221,7 +224,7 @@ class AutoholdPage extends React.Component { </Flex> </Flex> </PageSection> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Title headingLevel="h3"> <BuildIcon style={{ @@ -243,6 +246,7 @@ function mapStateToProps(state) { autohold: state.autoholds.autohold, tenant: state.tenant, isFetching: state.autoholds.isFetching, + preferences: state.preferences, } } diff --git a/web/src/pages/Build.jsx b/web/src/pages/Build.jsx index 0386f8eef..c66af1060 100644 --- a/web/src/pages/Build.jsx +++ b/web/src/pages/Build.jsx @@ -65,6 +65,7 @@ class BuildPage extends React.Component { activeTab: PropTypes.string.isRequired, location: PropTypes.object.isRequired, history: PropTypes.object.isRequired, + preferences: PropTypes.object, } state = { @@ -250,10 +251,10 @@ class BuildPage extends React.Component { return ( <> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Build build={build} active={activeTab} hash={hash} /> </PageSection> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Tabs isFilled activeKey={activeTab} @@ -314,7 +315,7 @@ class BuildPage extends React.Component { </Tabs> </PageSection> {!this.state.topOfPageVisible && ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Button onClick={scrollToTop} variant="primary" style={{position: 'fixed', bottom: 20, right: 20, zIndex: 1}}> Go to top of page <ArrowUpIcon/> </Button> @@ -362,6 +363,7 @@ function mapStateToProps(state, ownProps) { isFetchingManifest: state.build.isFetchingManifest, isFetchingOutput: state.build.isFetchingOutput, isFetchingLogfile: state.logfile.isFetching, + preferences: state.preferences, } } diff --git a/web/src/pages/Buildset.jsx b/web/src/pages/Buildset.jsx index b19aefb81..6e1cded57 100644 --- a/web/src/pages/Buildset.jsx +++ b/web/src/pages/Buildset.jsx @@ -38,6 +38,7 @@ class BuildsetPage extends React.Component { buildset: PropTypes.object, isFetching: PropTypes.bool.isRequired, fetchBuildset: PropTypes.func.isRequired, + preferences: PropTypes.object, } updateData = () => { @@ -105,10 +106,10 @@ class BuildsetPage extends React.Component { return ( <> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Buildset buildset={buildset} /> </PageSection> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Title headingLevel="h3"> <BuildIcon style={{ @@ -134,6 +135,7 @@ function mapStateToProps(state, ownProps) { buildset, tenant: state.tenant, isFetching: state.build.isFetching, + preferences: state.preferences, } } diff --git a/web/src/pages/Buildsets.jsx b/web/src/pages/Buildsets.jsx index 938309034..3ae3b772b 100644 --- a/web/src/pages/Buildsets.jsx +++ b/web/src/pages/Buildsets.jsx @@ -32,6 +32,7 @@ class BuildsetsPage extends React.Component { tenant: PropTypes.object, location: PropTypes.object, history: PropTypes.object, + preferences: PropTypes.object, } constructor(props) { @@ -230,7 +231,7 @@ class BuildsetsPage extends React.Component { const { buildsets, fetching, filters, resultsPerPage, currentPage, itemCount } = this.state return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <FilterToolbar filterCategories={this.filterCategories} onFilterChange={this.handleFilterChange} @@ -268,4 +269,7 @@ class BuildsetsPage extends React.Component { } } -export default connect((state) => ({ tenant: state.tenant }))(BuildsetsPage) +export default connect((state) => ({ + tenant: state.tenant, + preferences: state.preferences, +}))(BuildsetsPage) diff --git a/web/src/pages/ConfigErrors.jsx b/web/src/pages/ConfigErrors.jsx index b7a85d074..b43ebf562 100644 --- a/web/src/pages/ConfigErrors.jsx +++ b/web/src/pages/ConfigErrors.jsx @@ -18,7 +18,12 @@ import { connect } from 'react-redux' import { Icon } from 'patternfly-react' -import { PageSection, PageSectionVariants } from '@patternfly/react-core' +import { + PageSection, + PageSectionVariants, + List, + ListItem, +} from '@patternfly/react-core' import { fetchConfigErrorsAction } from '../actions/configErrors' @@ -26,7 +31,8 @@ class ConfigErrorsPage extends React.Component { static propTypes = { configErrors: PropTypes.object, tenant: PropTypes.object, - dispatch: PropTypes.func + dispatch: PropTypes.func, + preferences: PropTypes.object, } updateData = () => { @@ -36,7 +42,7 @@ class ConfigErrorsPage extends React.Component { render () { const { configErrors } = this.props return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <div className="pull-right"> {/* Lint warning jsx-a11y/anchor-is-valid */} {/* eslint-disable-next-line */} @@ -45,22 +51,22 @@ class ConfigErrorsPage extends React.Component { </a> </div> <div className="pull-left"> - <ul className="list-group"> + <List isPlain isBordered> {configErrors.map((item, idx) => { let ctxPath = item.source_context.path if (item.source_context.branch !== 'master') { ctxPath += ' (' + item.source_context.branch + ')' } return ( - <li className="list-group-item" key={idx}> + <ListItem key={idx}> <h3>{item.source_context.project} - {ctxPath}</h3> <p style={{whiteSpace: 'pre-wrap'}}> {item.error} </p> - </li> + </ListItem> ) })} - </ul> + </List> </div> </PageSection> ) @@ -69,5 +75,6 @@ class ConfigErrorsPage extends React.Component { export default connect(state => ({ tenant: state.tenant, - configErrors: state.configErrors.errors + configErrors: state.configErrors.errors, + preferences: state.preferences, }))(ConfigErrorsPage) diff --git a/web/src/pages/FreezeJob.jsx b/web/src/pages/FreezeJob.jsx index a78cc7b56..491a9a1ee 100644 --- a/web/src/pages/FreezeJob.jsx +++ b/web/src/pages/FreezeJob.jsx @@ -97,7 +97,8 @@ function FreezeJobPage(props) { collapsed={false} sortKeys={true} enableClipboard={false} - displayDataTypes={false}/> + displayDataTypes={false} + theme={props.preferences.darkMode ? 'tomorrow' : 'rjv-default'}/> </span> ) } @@ -133,12 +134,14 @@ FreezeJobPage.propTypes = { fetchFreezeJobIfNeeded: PropTypes.func, tenant: PropTypes.object, freezejob: PropTypes.object, + preferences: PropTypes.object, } function mapStateToProps(state) { return { tenant: state.tenant, freezejob: state.freezejob, + preferences: state.preferences, } } diff --git a/web/src/pages/Job.jsx b/web/src/pages/Job.jsx index efb4cfddc..f29ea383d 100644 --- a/web/src/pages/Job.jsx +++ b/web/src/pages/Job.jsx @@ -26,7 +26,8 @@ class JobPage extends React.Component { match: PropTypes.object.isRequired, tenant: PropTypes.object, remoteData: PropTypes.object, - dispatch: PropTypes.func + dispatch: PropTypes.func, + preferences: PropTypes.object, } updateData = (force) => { @@ -53,7 +54,7 @@ class JobPage extends React.Component { const tenantJobs = remoteData.jobs[this.props.tenant.name] const jobName = this.props.match.params.jobName return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode? PageSectionVariants.dark : PageSectionVariants.light}> {tenantJobs && tenantJobs[jobName] && <Job job={tenantJobs[jobName]} />} </PageSection> ) @@ -63,4 +64,5 @@ class JobPage extends React.Component { export default connect(state => ({ tenant: state.tenant, remoteData: state.job, + preferences: state.preferences, }))(JobPage) diff --git a/web/src/pages/Jobs.jsx b/web/src/pages/Jobs.jsx index d0b6a1466..6484b6f48 100644 --- a/web/src/pages/Jobs.jsx +++ b/web/src/pages/Jobs.jsx @@ -26,7 +26,8 @@ class JobsPage extends React.Component { static propTypes = { tenant: PropTypes.object, remoteData: PropTypes.object, - dispatch: PropTypes.func + dispatch: PropTypes.func, + preferences: PropTypes.object, } updateData = (force) => { @@ -51,8 +52,8 @@ class JobsPage extends React.Component { const jobs = remoteData.jobs[this.props.tenant.name] return ( - <PageSection variant={PageSectionVariants.light}> - <PageSection style={{paddingRight: '5px'}}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> + <PageSection variant={PageSectionVariants.light} style={{paddingRight: '5px'}}> <Fetchable isFetching={remoteData.isFetching} fetchCallback={this.updateData} @@ -70,4 +71,5 @@ class JobsPage extends React.Component { export default connect(state => ({ tenant: state.tenant, remoteData: state.jobs, + preferences: state.preferences, }))(JobsPage) diff --git a/web/src/pages/OpenApi.jsx b/web/src/pages/OpenApi.jsx index 7ccf5f34c..ec8ef8c56 100644 --- a/web/src/pages/OpenApi.jsx +++ b/web/src/pages/OpenApi.jsx @@ -26,7 +26,8 @@ class OpenApiPage extends React.Component { static propTypes = { tenant: PropTypes.object, remoteData: PropTypes.object, - dispatch: PropTypes.func + dispatch: PropTypes.func, + preferences: PropTypes.object, } updateData = (force) => { @@ -51,7 +52,7 @@ class OpenApiPage extends React.Component { render() { return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <div id="swaggerContainer" /> </PageSection> ) @@ -61,4 +62,5 @@ class OpenApiPage extends React.Component { export default connect(state => ({ tenant: state.tenant, remoteData: state.openapi, + preferences: state.preferences, }))(OpenApiPage) diff --git a/web/src/pages/Project.jsx b/web/src/pages/Project.jsx index 06e8612c7..0c808ec09 100644 --- a/web/src/pages/Project.jsx +++ b/web/src/pages/Project.jsx @@ -46,7 +46,7 @@ function ProjectPage(props) { return ( <> - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <TextContent> <Text component="h2">Project {projectName}</Text> <Fetchable @@ -70,12 +70,14 @@ ProjectPage.propTypes = { tenant: PropTypes.object, remoteData: PropTypes.object, fetchProjectIfNeeded: PropTypes.func, + preferences: PropTypes.object, } function mapStateToProps(state) { return { tenant: state.tenant, remoteData: state.project, + preferences: state.preferences, } } diff --git a/web/src/pages/Semaphore.jsx b/web/src/pages/Semaphore.jsx index a0ae8ddca..2cfdfb73f 100644 --- a/web/src/pages/Semaphore.jsx +++ b/web/src/pages/Semaphore.jsx @@ -25,7 +25,7 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core' import { fetchSemaphoresIfNeeded } from '../actions/semaphores' import Semaphore from '../containers/semaphore/Semaphore' -function SemaphorePage({ match, semaphores, tenant, fetchSemaphoresIfNeeded, isFetching }) { +function SemaphorePage({ match, semaphores, tenant, fetchSemaphoresIfNeeded, isFetching, preferences }) { const semaphoreName = match.params.semaphoreName @@ -38,7 +38,7 @@ function SemaphorePage({ match, semaphores, tenant, fetchSemaphoresIfNeeded, isF e => e.name === semaphoreName) : undefined return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Title headingLevel="h2"> Details for Semaphore <span style={{color: 'var(--pf-global--primary-color--100)'}}>{semaphoreName}</span> </Title> @@ -55,6 +55,7 @@ SemaphorePage.propTypes = { tenant: PropTypes.object.isRequired, isFetching: PropTypes.bool.isRequired, fetchSemaphoresIfNeeded: PropTypes.func.isRequired, + preferences: PropTypes.object, } const mapDispatchToProps = { fetchSemaphoresIfNeeded } @@ -63,6 +64,7 @@ function mapStateToProps(state) { tenant: state.tenant, semaphores: state.semaphores.semaphores, isFetching: state.semaphores.isFetching, + preferences: state.preferences, } } diff --git a/web/src/pages/Status.jsx b/web/src/pages/Status.jsx index ac3dc7840..e61ceb3e2 100644 --- a/web/src/pages/Status.jsx +++ b/web/src/pages/Status.jsx @@ -197,6 +197,7 @@ class StatusPage extends React.Component { <FormGroup controlId='status'> <FormControl type='text' + className="pf-c-form-control" placeholder='change or project name' defaultValue={filter} inputRef={i => this.filter = i} @@ -222,7 +223,7 @@ class StatusPage extends React.Component { </Form> ) return ( - <PageSection variant={PageSectionVariants.light}> + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <div style={{display: 'flex', float: 'right'}}> <Fetchable isFetching={remoteData.isFetching} diff --git a/web/src/pages/Stream.jsx b/web/src/pages/Stream.jsx index df2a2ad96..f058d29a2 100644 --- a/web/src/pages/Stream.jsx +++ b/web/src/pages/Stream.jsx @@ -31,7 +31,8 @@ class StreamPage extends React.Component { static propTypes = { match: PropTypes.object.isRequired, location: PropTypes.object.isRequired, - tenant: PropTypes.object + tenant: PropTypes.object, + preferences: PropTypes.object, } state = { @@ -167,10 +168,11 @@ class StreamPage extends React.Component { render () { return ( - <PageSection variant={PageSectionVariants.light} > + <PageSection variant={this.props.preferences.darkMode ? PageSectionVariants.dark : PageSectionVariants.light}> <Form inline> <FormGroup controlId='stream'> <FormControl + className="pf-c-form-control" type='text' placeholder='search' onKeyPress={this.handleKeyPress} @@ -201,4 +203,7 @@ class StreamPage extends React.Component { } -export default connect(state => ({tenant: state.tenant}))(StreamPage) +export default connect(state => ({ + tenant: state.tenant, + preferences: state.preferences, +}))(StreamPage) |