-
Notifications
You must be signed in to change notification settings - Fork 287
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add theme management tools (#2037)
* feat: theme toggle control adds a new component to allow switching in the ui between light and dark modes adds a theme toggle to all stories by default updates developer test page to include toggle adds helper functions to assist with theme changing using fluentui web components theming tools
- Loading branch information
1 parent
d801658
commit 19339d7
Showing
16 changed files
with
503 additions
and
16 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
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
7 changes: 7 additions & 0 deletions
7
packages/mgt-components/src/components/mgt-person/mgt-person-types.ts
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
74 changes: 74 additions & 0 deletions
74
packages/mgt-components/src/components/mgt-theme-toggle/mgt-theme-toggle.tests.ts
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,74 @@ | ||
/** | ||
* ------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. | ||
* See License in the project root for license information. | ||
* ------------------------------------------------------------------------------------------- | ||
*/ | ||
// import the mock for media match first to ensure it's hoisted and available for our dependencies | ||
import './mock-media-match'; | ||
import { screen } from 'testing-library__dom'; | ||
import { fixture } from '@open-wc/testing-helpers'; | ||
import './mgt-theme-toggle'; | ||
|
||
describe('mgt-theme-toggle - tests', () => { | ||
it('should render', async () => { | ||
await fixture('<mgt-theme-toggle></mgt-theme-toggle>'); | ||
const toggle = await screen.findByRole('switch'); | ||
expect(toggle).not.toBeNull(); | ||
}); | ||
it("should emit darkmodechanged with the current 'checked' state on click", async () => { | ||
let darkModeState = false; | ||
const element = await fixture('<mgt-theme-toggle></mgt-theme-toggle>'); | ||
const toggle: HTMLInputElement = await screen.findByRole('switch'); | ||
expect(toggle).not.toBeNull(); | ||
element.addEventListener('darkmodechanged', (e: CustomEvent<boolean>) => { | ||
darkModeState = e.detail; | ||
}); | ||
expect(darkModeState).toBe(false); | ||
expect(toggle.checked).toBe(false); | ||
toggle.click(); | ||
expect(darkModeState).toBe(true); | ||
expect(toggle.checked).toBe(true); | ||
toggle.click(); | ||
expect(darkModeState).toBe(false); | ||
expect(toggle.checked).toBe(false); | ||
}); | ||
|
||
it('should have a checked switch if mode is dark', async () => { | ||
await fixture('<mgt-theme-toggle mode="dark"></mgt-theme-toggle>'); | ||
const toggle = await screen.findByRole('switch'); | ||
expect(toggle).not.toBeNull(); | ||
expect(toggle.getAttribute('aria-checked')).toBe('true'); | ||
expect(toggle.getAttribute('checked')).toBe('true'); | ||
}); | ||
|
||
it('should not have a checked switch if mode is light', async () => { | ||
await fixture('<mgt-theme-toggle></mgt-theme-toggle>'); | ||
const toggle = await screen.findByRole('switch'); | ||
expect(toggle).not.toBeNull(); | ||
expect(toggle.getAttribute('aria-checked')).toBe('false'); | ||
expect(toggle.getAttribute('checked')).toBe('false'); | ||
}); | ||
|
||
it('should have a checked switch if user prefers dark mode and no mode is set', async () => { | ||
// redefine matchMedia to return true | ||
Object.defineProperty(window, 'matchMedia', { | ||
writable: true, | ||
value: jest.fn().mockImplementation(query => ({ | ||
matches: true, | ||
media: query, | ||
onchange: null, | ||
addListener: jest.fn(), // deprecated | ||
removeListener: jest.fn(), // deprecated | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn(), | ||
dispatchEvent: jest.fn() | ||
})) | ||
}); | ||
await fixture('<mgt-theme-toggle></mgt-theme-toggle>'); | ||
const toggle = await screen.findByRole('switch'); | ||
expect(toggle).not.toBeNull(); | ||
expect(toggle.getAttribute('aria-checked')).toBe('true'); | ||
expect(toggle.getAttribute('checked')).toBe('true'); | ||
}); | ||
}); |
108 changes: 108 additions & 0 deletions
108
packages/mgt-components/src/components/mgt-theme-toggle/mgt-theme-toggle.ts
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,108 @@ | ||
/** | ||
* ------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. | ||
* See License in the project root for license information. | ||
* ------------------------------------------------------------------------------------------- | ||
*/ | ||
|
||
import { html, TemplateResult } from 'lit'; | ||
import { property } from 'lit/decorators.js'; | ||
import { customElement, MgtBaseComponent } from '@microsoft/mgt-element'; | ||
import { fluentSwitch } from '@fluentui/web-components/dist/esm/switch'; | ||
import { registerFluentComponents } from '../../utils/FluentComponents'; | ||
import { applyTheme } from '../../styles/theme-manager'; | ||
import { strings } from './strings'; | ||
|
||
registerFluentComponents(fluentSwitch); | ||
|
||
/** | ||
* Toggle to switch between light and dark mode | ||
* Will detect browser preference and set accordingly or dark mode can be forced | ||
* | ||
* @fires {CustomEvent<boolean>} darkmodechanged - Fired when dark mode is toggled by a user action | ||
* | ||
* @class MgtDarkToggle | ||
* @extends {MgtBaseComponent} | ||
*/ | ||
@customElement('theme-toggle') | ||
class MgtThemeToggle extends MgtBaseComponent { | ||
constructor() { | ||
super(); | ||
const prefersDarkMode = window.matchMedia('(prefers-color-scheme:dark)').matches; | ||
this.darkModeActive = prefersDarkMode; | ||
this.applyTheme(this.darkModeActive); | ||
} | ||
/** | ||
* Provides strings for localization | ||
* | ||
* @readonly | ||
* @protected | ||
* @memberof MgtDarkToggle | ||
*/ | ||
protected get strings() { | ||
return strings; | ||
} | ||
|
||
/** | ||
* Controls whether dark mode is active | ||
* | ||
* @type {boolean} | ||
* @memberof MgtDarkToggle | ||
*/ | ||
@property({ | ||
attribute: 'mode', | ||
reflect: true, | ||
type: String, | ||
converter: { | ||
fromAttribute(value: string) { | ||
return value === 'dark'; | ||
}, | ||
toAttribute(value: boolean) { | ||
return value ? 'dark' : 'light'; | ||
} | ||
} | ||
}) | ||
public darkModeActive: boolean; | ||
|
||
/** | ||
* Fires after a component is updated. | ||
* Allows a component to trigger side effects after updating. | ||
* | ||
* @param {Map<string, any>} changedProperties | ||
* @memberof MgtDarkToggle | ||
*/ | ||
updated(changedProperties: Map<string, any>): void { | ||
if (changedProperties.has('darkModeActive')) { | ||
this.applyTheme(this.darkModeActive); | ||
} | ||
} | ||
|
||
/** | ||
* renders the component | ||
* | ||
* @return {TemplateResult} | ||
* @memberof MgtDarkToggle | ||
*/ | ||
render(): TemplateResult { | ||
return html` | ||
<fluent-switch checked=${this.darkModeActive} @change=${this.onSwitchChanged}> | ||
<span slot="checked-message">${strings.on}</span> | ||
<span slot="unchecked-message">${strings.off}</span> | ||
<label for="direction-switch">${strings.label}</label> | ||
</fluent-switch> | ||
`; | ||
} | ||
|
||
private onSwitchChanged(e: Event) { | ||
this.darkModeActive = (e.target as HTMLInputElement).checked; | ||
this.fireCustomEvent('darkmodechanged', this.darkModeActive); | ||
} | ||
|
||
private applyTheme(active: boolean) { | ||
const targetTheme = active ? 'dark' : 'light'; | ||
applyTheme(targetTheme); | ||
|
||
document.body.classList.remove('mgt-dark-mode', 'mgt-light-mode'); | ||
document.body.classList.add(`mgt-${targetTheme}-mode`); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
packages/mgt-components/src/components/mgt-theme-toggle/mock-media-match.ts
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,20 @@ | ||
/** | ||
* ------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. | ||
* See License in the project root for license information. | ||
* ------------------------------------------------------------------------------------------- | ||
*/ | ||
|
||
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() | ||
})) | ||
}); |
12 changes: 12 additions & 0 deletions
12
packages/mgt-components/src/components/mgt-theme-toggle/strings.ts
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,12 @@ | ||
/** | ||
* ------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. | ||
* See License in the project root for license information. | ||
* ------------------------------------------------------------------------------------------- | ||
*/ | ||
|
||
export const strings = { | ||
label: 'Color mode:', | ||
on: 'Dark', | ||
off: 'Light' | ||
}; |
Oops, something went wrong.