-
Notifications
You must be signed in to change notification settings - Fork 635
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8040 from jonboiser/custom-channels-theming
Add ContentModal to display resources within the context of a Custom Channel Renderer
- Loading branch information
Showing
17 changed files
with
675 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<template> | ||
|
||
<transition name="backdrop" appear> | ||
<div | ||
class="backdrop" | ||
:class="{ 'has-transitions': transitions }" | ||
@click="$emit('click')" | ||
> | ||
<slot></slot> | ||
</div> | ||
</transition> | ||
|
||
</template> | ||
|
||
|
||
<script> | ||
export default { | ||
name: 'Backdrop', | ||
props: { | ||
transitions: { | ||
// If true, backdrop will have enter/leave transitions | ||
type: Boolean, | ||
default: false, | ||
}, | ||
}, | ||
}; | ||
</script> | ||
|
||
|
||
<style lang="scss" scoped> | ||
.backdrop { | ||
position: fixed; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: 0; | ||
width: 100%; | ||
height: 100%; | ||
background: rgba(0, 0, 0, 0.7); | ||
background-attachment: fixed; | ||
} | ||
.has-transitions.backdrop-enter { | ||
opacity: 0; | ||
} | ||
.has-transitions.backdrop-enter-to { | ||
opacity: 1; | ||
} | ||
.has-transitions.backdrop-enter-active { | ||
transition: opacity 0.2s ease-in-out; | ||
} | ||
.has-transitions.backdrop-leave { | ||
opacity: 1; | ||
} | ||
.has-transitions.backdrop-leave-to { | ||
opacity: 0; | ||
} | ||
.has-transitions.backdrop-leave-active { | ||
transition: opacity 0.2s ease-in-out; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
kolibri/plugins/learn/assets/src/utils/__test__/themes.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import logging from 'kolibri.lib.logging'; | ||
import { validateTheme } from '../themes'; | ||
|
||
const defaultTheme = { | ||
appBarColor: null, | ||
textColor: null, | ||
backdropColor: null, | ||
backgroundColor: null, | ||
}; | ||
|
||
describe('theme validator', () => { | ||
let logError = jest.fn(); | ||
let logWarn = jest.fn(); | ||
|
||
beforeAll(() => { | ||
const logger = logging.getLogger('theme validation'); | ||
logError = jest.spyOn(logger, 'error').mockImplementation(); | ||
logWarn = jest.spyOn(logger, 'warn').mockImplementation(); | ||
}); | ||
|
||
it('accepts valid colors for the theme', () => { | ||
const validTheme = { | ||
appBarColor: 'rgb(247, 138, 224)', | ||
textColor: '#282a36', | ||
backdropColor: 'lightblue', | ||
backgroundColor: 'rgba(40, 42, 54, 1.00)', | ||
}; | ||
const theme = validateTheme(validTheme); | ||
expect(logWarn).not.toHaveBeenCalled(); | ||
expect(logError).not.toHaveBeenCalled(); | ||
expect(theme).toEqual({ | ||
appBarColor: 'rgb(247, 138, 224)', | ||
textColor: '#282a36', | ||
backdropColor: 'rgba(173, 216, 230, 0.7)', // opacity is added to backdrop color | ||
backgroundColor: 'rgba(40, 42, 54, 1.00)', | ||
}); | ||
}); | ||
|
||
const invalidThemeTests = [ | ||
['appBarColor', 'rgb()'], | ||
['textColor', 'purplish'], | ||
['backdropColor', 100], | ||
['backgroundColor', '#YYTTXX'], | ||
]; | ||
it.each(invalidThemeTests)('handles invalid setting of %s to %s', (key, color) => { | ||
const expectedLog = `invalid color '${color}' provided for '${key}'`; | ||
const theme = validateTheme({ ...defaultTheme, [key]: color }); | ||
expect(logError).toHaveBeenCalledWith(expectedLog); | ||
// No changes are made | ||
expect(theme).toEqual(defaultTheme); | ||
}); | ||
|
||
it('logs an error if an invalid theme key is provided', () => { | ||
const theme = validateTheme({ fontFamily: 'Roboto Mono' }); | ||
expect(logError).toHaveBeenCalledWith(`'fontFamily' is not a valid custom theme option`); | ||
expect(theme).toEqual(defaultTheme); | ||
}); | ||
|
||
it('logs a warning if the chosen colors do not pass WCAG Level AA', () => { | ||
const theme = validateTheme({ | ||
textColor: 'white', | ||
appBarColor: 'rgb(238, 238, 238)', // a light grey | ||
}); | ||
expect(logWarn).toHaveBeenCalledWith( | ||
"'textColor' and 'appBarColor' do not provide enough contrast and do not pass WCAG Level AA guidelines" | ||
); | ||
expect(theme).toEqual({ | ||
textColor: 'white', | ||
appBarColor: 'rgb(238, 238, 238)', // a light grey | ||
backdropColor: null, | ||
backgroundColor: null, | ||
}); | ||
}); | ||
|
||
const requiredOptionsTests = [ | ||
['black', undefined], | ||
[undefined, 'white'], | ||
]; | ||
it.each(requiredOptionsTests)( | ||
'logs an error if valid values for both textColor and appBarColor are not provided', | ||
(appBarColor, textColor) => { | ||
const theme = validateTheme({ | ||
textColor, | ||
appBarColor, | ||
}); | ||
expect(logError).toHaveBeenCalledWith( | ||
`valid values for 'textColor' and 'appBarColor' must be provided` | ||
); | ||
expect(theme).toEqual(defaultTheme); | ||
} | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import tinycolor from 'tinycolor2'; | ||
import logging from 'kolibri.lib.logging'; | ||
|
||
const logger = logging.getLogger('theme validation'); | ||
|
||
const validKeys = ['appBarColor', 'textColor', 'backdropColor', 'backgroundColor']; | ||
|
||
const defaultTheme = { | ||
appBarColor: null, | ||
textColor: null, | ||
backdropColor: null, | ||
backgroundColor: null, | ||
}; | ||
|
||
export function validateTheme(theme) { | ||
const updatedTheme = { | ||
...defaultTheme, | ||
}; | ||
for (let key in theme) { | ||
const color = theme[key]; | ||
if (!color) { | ||
continue; | ||
} else if (!validKeys.includes(key)) { | ||
logger.error(`'${key}' is not a valid custom theme option`); | ||
} else if (!tinycolor(color).isValid()) { | ||
logger.error(`invalid color '${color}' provided for '${key}'`); | ||
} else { | ||
updatedTheme[key] = color; | ||
} | ||
} | ||
|
||
// if backdrop color has no transparency, give it an alpha of 0.7 | ||
if (updatedTheme.backdropColor) { | ||
const bdcolor = tinycolor(updatedTheme.backdropColor); | ||
bdcolor.setAlpha(0.7); | ||
updatedTheme.backdropColor = bdcolor.toRgbString(); | ||
} | ||
|
||
// if updated theme does not have valid values for both appBarColor and textColor, | ||
// log an error and reset the missing options to null | ||
if ((theme.textColor && !theme.appBarColor) || (!theme.textColor && theme.appBarColor)) { | ||
logger.error(`valid values for 'textColor' and 'appBarColor' must be provided`); | ||
updatedTheme.textColor = null; | ||
updatedTheme.appBarColor = null; | ||
} | ||
|
||
// check contrast and log a warning if it's not WCAG Level AA-compliant | ||
if (updatedTheme.textColor && updatedTheme.appBarColor) { | ||
if (!tinycolor.isReadable(updatedTheme.textColor, updatedTheme.appBarColor)) { | ||
logger.warn( | ||
`'textColor' and 'appBarColor' do not provide enough contrast and do not pass WCAG Level AA guidelines` | ||
); | ||
} | ||
} | ||
|
||
return updatedTheme; | ||
} |
Oops, something went wrong.