Skip to content

Commit

Permalink
Merge pull request #2002 from codecrafters-io/add-dark-mode-toggle
Browse files Browse the repository at this point in the history
add dark mode toggle
  • Loading branch information
rohitpaulk authored Jul 22, 2024
2 parents f98eac5 + 0326d07 commit 77ba495
Show file tree
Hide file tree
Showing 27 changed files with 157 additions and 79 deletions.
2 changes: 1 addition & 1 deletion app/components/course-page/test-results-bar.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div
class="bg-gray-900 border-t border-gray-800 dark flex flex-col items-center"
class="bg-black border-t border-gray-800 dark flex flex-col items-center"
style={{if this.isExpanded this.customHeight}}
data-test-test-results-bar
...attributes
Expand Down
19 changes: 19 additions & 0 deletions app/components/dark-mode-toggle-option.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<TertiaryButton class="pl-2 {{if @isSelected 'border-teal-500 hover:bg-white'}}" ...attributes data-test-dark-mode-toggle-option>
<div class="flex items-center text-sm sm:text-base {{if @isSelected 'text-teal-500' 'text-gray-600'}}">
<AnimatedContainer class="overflow-hidden">
{{#animated-if @isSelected rules=this.rules duration=100}}
{{! wrapping div is required for animations to work }}
<div class="flex">
{{svg-jar "check-circle" class="w-4 h-4 mr-1.5 text-teal-500 flex-shrink-0"}}
</div>
{{else}}
{{! wrapping div is required for animations to work }}
<div class="flex">
{{svg-jar "check" class="w-4 h-4 mr-1.5 text-gray-400 flex-shrink-0"}}
</div>
{{/animated-if}}
</AnimatedContainer>

{{yield}}
</div>
</TertiaryButton>
31 changes: 31 additions & 0 deletions app/components/dark-mode-toggle-option.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Component from '@glimmer/component';
import { toRight, toLeft } from 'ember-animated/transitions/move-over';

export interface Signature {
Element: HTMLButtonElement;

Args: {
isSelected: boolean;
};

Blocks: { default: [] };
}

export default class DarkModeToggleOptionComponent extends Component<Signature> {
toRight = toRight;
toLeft = toLeft;

rules({ newItems }: { newItems: unknown[] }) {
if (newItems[0]) {
return toRight;
} else {
return toLeft;
}
}
}

declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
DarkModeToggleOption: typeof DarkModeToggleOptionComponent;
}
}
12 changes: 4 additions & 8 deletions app/components/dark-mode-toggle.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
{{#each this.possiblePreferences as |preference|}}
{{#if (eq this.darkMode.localStoragePreference preference)}}
<PrimaryButton @size="small" class="capitalize" {{on "click" (fn this.setPreference preference)}}>{{or preference "unset"}}</PrimaryButton>
{{else}}
<TertiaryButton @size="small" class="capitalize" {{on "click" (fn this.setPreference preference)}}>{{or preference "unset"}}</TertiaryButton>
{{/if}}
{{/each}}

{{yield}}
<DarkModeToggleOption @isSelected={{eq this.darkMode.localStoragePreference preference}} {{on "click" (fn this.setPreference preference)}}>
{{or preference "unset"}}
</DarkModeToggleOption>
{{/each}}
4 changes: 2 additions & 2 deletions app/components/dark-mode-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import type { LocalStoragePreference } from 'codecrafters-frontend/services/dark

export interface Signature {
Element: Element;
Blocks: { default?: [] };
}

export default class DarkModeToggleComponent extends Component<Signature> {
@service declare darkMode: DarkModeService;

possiblePreferences: LocalStoragePreference[] = ['system', 'dark', 'light', null];

@action setPreference(newValue: LocalStoragePreference) {
@action
setPreference(newValue: LocalStoragePreference) {
this.darkMode.updateLocalStoragePreference(newValue);
}
}
Expand Down
14 changes: 7 additions & 7 deletions app/components/pill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ type Signature = {
export default class PillComponent extends Component<Signature> {
get colorClasses() {
return {
green: 'bg-green-50 text-green-700 ring-green-600/20',
yellow: 'bg-yellow-50 text-yellow-700 ring-yellow-600/20',
red: 'bg-red-50 text-red-700 ring-red-600/20',
blue: 'bg-blue-50 text-blue-700 ring-blue-600/20',
gray: 'bg-gray-50 text-gray-700 ring-gray-600/20',
'dark-gray': 'bg-gray-200 text-gray-700 ring-gray-600/20',
white: 'bg-white text-gray-600 ring-gray-600/20',
green: 'bg-green-50 text-green-700 ring-green-600/20 dark:bg-green-950 dark:text-green-300 dark:ring-green-400/20',
yellow: 'bg-yellow-50 text-yellow-700 ring-yellow-600/20 dark:bg-yellow-950 dark:text-yellow-300 dark:ring-yellow-400/20',
red: 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-950 dark:text-red-300 dark:ring-red-400/20',
blue: 'bg-blue-50 text-blue-700 ring-blue-600/20 dark:bg-blue-950 dark:text-blue-300 dark:ring-blue-400/20',
gray: 'bg-gray-50 text-gray-700 ring-gray-600/20 dark:bg-gray-900 dark:text-gray-300 dark:ring-gray-400/20',
'dark-gray': 'bg-gray-200 text-gray-700 ring-gray-600/20 dark:bg-gray-800 dark:text-gray-300 dark:ring-gray-400/20',
white: 'bg-white text-gray-600 ring-gray-600/20 dark:bg-gray-950 dark:text-gray-300 dark:ring-gray-400/20',
}[this.args.color];
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/components/settings/form-divider.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="h-px bg-gray-100 my-6"></div>
<div class="h-px bg-gray-100 my-6 dark:bg-gray-900"></div>
4 changes: 2 additions & 2 deletions app/components/settings/form-section.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="grid grid-cols-1 sm:grid-cols-2" ...attributes>
<div class="pb-6 pr-0 sm:pb-0 sm:pr-4">
<div class="text-gray-700 font-semibold text-lg">{{@title}}</div>
<div class="text-gray-400 text-xs mt-1">{{@description}}</div>
<div class="text-gray-700 dark:text-gray-200 font-semibold text-lg">{{@title}}</div>
<div class="text-gray-400 dark:text-gray-600 text-xs mt-1">{{@description}}</div>

{{#if (has-block "belowDescription")}}
<div class="mt-2">
Expand Down
4 changes: 2 additions & 2 deletions app/components/settings/profile-page/about-section.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{{! template-lint-disable require-input-label }}
<Textarea
@value={{@user.profileDescriptionMarkdown}}
class="text-sm rounded px-3 py-2 placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-teal-500 border border-gray-300 shadow-sm w-full"
class="text-sm rounded px-3 py-2 placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-teal-500 border border-gray-300 shadow-sm w-full dark:border-gray-800 dark:bg-black"
placeholder="I'm a software engineer who loves building things. Here are some of my projects..."
rows="3"
required
Expand All @@ -24,7 +24,7 @@
Saved!
</span>
{{else}}
<span class="text-gray-400">
<span class="text-gray-400 dark:text-gray-600">
Displayed on your profile page. Markdown supported.
</span>
{{/if}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{{on "click" this.handleToggle}}
data-test-anonymous-mode-toggle
/>
<div class="text-gray-600 text-sm">Enable anonymous mode</div>
<div class="text-gray-600 dark:text-gray-400 text-sm">Enable anonymous mode</div>

{{#unless @user.canAccessMembershipBenefits}}
{{#if @user.hasAnonymousModeEnabled}}
Expand All @@ -25,7 +25,7 @@
{{/if}}
{{/unless}}
</div>
<p class="text-gray-400 text-xs mt-4 leading-relaxed">
<p class="text-gray-400 dark:text-gray-600 text-xs mt-4 leading-relaxed">
When anonymous mode is enabled, your GitHub username &amp; avatar will be hidden on CodeCrafters. Your code examples &amp; profile page will
still be visible to the public, but under a random username.
<a href="https://codecrafters.io/anon" target="_blank" class="underline" rel="noopener noreferrer">Learn more.</a>
Expand Down
2 changes: 1 addition & 1 deletion app/components/settings/profile-page/avatar-section.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<EmberTooltip @text="This image is synced with your GitHub account." />
{{/if}}
</div>
<div class="text-gray-400 text-xs mt-2">
<div class="text-gray-400 dark:text-gray-600 text-xs mt-2">
{{#if @user.hasAnonymousModeEnabled}}
Your profile has anonymous mode enabled. Your GitHub avatar is not visible to others.
{{else}}
Expand Down
15 changes: 15 additions & 0 deletions app/components/settings/profile-page/dark-mode-section.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Settings::FormSection @title="Dark Mode" @description="Choose when CodeCrafters should use dark mode.">
<:belowDescription>
<Pill @color="green">
{{svg-jar "shield-check" class="w-4 h-4 text-green-500 mr-1"}}
Staff only

<EmberTooltip @text="This feature is only available to CodeCrafters staff." />
</Pill>
</:belowDescription>
<:default>
<div class="inline-flex items-center gap-3">
<DarkModeToggle />
</div>
</:default>
</Settings::FormSection>
13 changes: 13 additions & 0 deletions app/components/settings/profile-page/dark-mode-section.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Component from '@glimmer/component';

interface Signature {
Element: HTMLDivElement;
}

export default class DarkModeSectionComponent extends Component<Signature> {}

declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
'Settings::ProfilePage::DarkModeSection': typeof DarkModeSectionComponent;
}
}
4 changes: 2 additions & 2 deletions app/components/settings/profile-page/username-section.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{{! template-lint-disable require-input-label }}
<Input
@value={{@user.username}}
class="text-sm rounded px-3 py-2 bg-gray-50 border w-full text-gray-500 font-mono cursor-not-allowed"
class="text-sm rounded px-3 py-2 bg-gray-50 border dark:border-gray-800 w-full text-gray-500 font-mono cursor-not-allowed dark:bg-gray-900"
placeholder="cool_username"
disabled={{true}}
/>
Expand All @@ -15,7 +15,7 @@
{{/if}}
</div>

<div class="text-gray-400 text-xs mt-1">
<div class="text-gray-400 dark:text-gray-600 text-xs mt-2">
{{#if @user.hasAnonymousModeEnabled}}
Your profile has anonymous mode enabled. Your GitHub username is not visible to others.
{{else}}
Expand Down
2 changes: 1 addition & 1 deletion app/components/tertiary-button.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<BaseButton
@size={{@size}}
@isDisabled={{@isDisabled}}
class="transition-colors duration-75 dark:bg-gray-800 bg-white hover:bg-gray-50 dark:text-gray-300 dark:hover:text-white text-gray-800 dark:border-gray-600 border-gray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
class="transition-colors duration-75 bg-white dark:bg-black hover:bg-gray-50 dark:text-gray-400 text-gray-800 border-gray-300 dark:border-gray-800 dark:hover:border-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
...attributes
>
{{yield}}
Expand Down
4 changes: 2 additions & 2 deletions app/components/tertiary-link-button.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
@models={{@models}}
@model={{@model}}
@size={{@size}}
@isDisabled={{@isDisabled}}
@query={{@query}}
@shouldOpenInNewTab={{@shouldOpenInNewTab}}
class="bg-white dark:bg-transparent text-gray-900 dark:text-gray-200 border-gray-300 dark:border-gray-300 light:hover:bg-gray-50 dark:hover:text-white dark:hover:border-gray-200
{{if @isDisabled 'cursor-not-allowed opacity-50'}}"
class="transition-colors duration-75 bg-white dark:bg-black hover:bg-gray-50 dark:text-gray-400 text-gray-800 border-gray-300 dark:border-gray-800 dark:hover:border-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
...attributes
>
{{yield}}
Expand Down
6 changes: 3 additions & 3 deletions app/components/toggle.hbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<button
type="button"
class="{{if @isOn 'bg-teal-600' 'bg-gray-200'}}
class="{{if @isOn 'bg-teal-600' 'bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 hover:dark:bg-gray-600'}}
{{if @isDisabled 'opacity-50 cursor-not-allowed' 'cursor-pointer'}}
{{if this.sizeIsSmall 'h-3.5 w-7'}}
{{if this.sizeIsRegular 'h-6 w-11'}}
relative inline-flex flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-100 ease-in-out focus:outline-none focus:ring-2 focus:ring-teal-600 focus:ring-offset-2"
relative inline-flex flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-100 ease-in-out focus:outline-none focus:ring-2 focus:ring-teal-600 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-900"
role="switch"
aria-checked="{{if @isOn 'true' 'false'}}"
...attributes
Expand All @@ -14,7 +14,7 @@
{{if (and @isOn this.sizeIsRegular) 'translate-x-5' 'translate-x-0'}}
{{if this.sizeIsSmall 'h-2.5 w-2.5'}}
{{if this.sizeIsRegular 'h-5 w-5'}}
pointer-events-none relative inline-block transform rounded-full bg-white shadow ring-0 transition duration-100 ease-in-out"
pointer-events-none relative inline-block transform rounded-full shadow ring-0 transition duration-100 ease-in-out bg-white dark:bg-black"
>
<span
class="{{if
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Component from '@glimmer/component';
import fade from 'ember-animated/transitions/fade';
import { toRight, toLeft } from 'ember-animated/transitions/move-over';

type Signature = {
Expand All @@ -15,7 +14,6 @@ type Signature = {
};

export default class SelectableItemComponent extends Component<Signature> {
transition = fade;
toRight = toRight;
toLeft = toLeft;

Expand Down
5 changes: 5 additions & 0 deletions app/routes/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service';
import type UserModel from 'codecrafters-frontend/models/user';
import type AuthenticatorService from 'codecrafters-frontend/services/authenticator';
import BaseRoute from 'codecrafters-frontend/utils/base-route';
import RouteInfoMetadata, { RouteColorScheme } from 'codecrafters-frontend/utils/route-info-metadata';

export type ModelType = {
user: UserModel;
Expand All @@ -12,6 +13,10 @@ export default class SettingsRoute extends BaseRoute {
@service declare authenticator: AuthenticatorService;
@service declare router: RouterService;

buildRouteInfoMetadata(): RouteInfoMetadata {
return new RouteInfoMetadata({ colorScheme: RouteColorScheme.Both });
}

async model(): Promise<ModelType> {
await this.authenticator.authenticate();

Expand Down
2 changes: 1 addition & 1 deletion app/templates/application.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{{page-title "CodeCrafters"}}

<div class="application-container bg-gray-50 min-h-screen {{if this.darkMode.isEnabled 'dark'}}">
<div class="application-container min-h-screen {{if this.darkMode.isEnabled 'dark bg-black' 'bg-gray-50'}}">
{{#if this.layout.shouldShowHeader}}
<Header />
{{/if}}
Expand Down
4 changes: 2 additions & 2 deletions app/templates/settings.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{page-title "Settings"}}

<div class="container mx-auto lg:max-w-screen-lg pt-10 pb-48 px-4 md:px-6">
<div class="font-semibold text-xl text-gray-700 mb-4">Settings</div>
<div class="font-semibold text-xl text-gray-700 dark:text-gray-100 mb-4">Settings</div>

<ul class="flex items-center overflow-x-auto pb-4 md:pb-6">
{{#each this.tabs as |tab|}}
Expand All @@ -11,7 +11,7 @@
{{/each}}
</ul>

<div class="bg-white rounded-md p-4 md:p-6 border">
<div class="bg-white dark:bg-black rounded-md p-4 md:p-6 border dark:border-gray-800">
{{outlet}}
</div>
</div>
7 changes: 6 additions & 1 deletion app/templates/settings/profile.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
<Settings::FormDivider />
<Settings::ProfilePage::AvatarSection @user={{@model.user}} />
<Settings::FormDivider />
<Settings::ProfilePage::AboutSection @user={{@model.user}} />
<Settings::ProfilePage::AboutSection @user={{@model.user}} />

{{#if @model.user.isStaff}}
<Settings::FormDivider />
<Settings::ProfilePage::DarkModeSection />
{{/if}}
15 changes: 14 additions & 1 deletion tests/acceptance/settings-page/profile-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import testScenario from 'codecrafters-frontend/mirage/scenarios/test';
import { assertTooltipContent } from 'ember-tooltips/test-support';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'codecrafters-frontend/tests/helpers';
import { signIn, signInAsSubscriber } from 'codecrafters-frontend/tests/support/authentication-helpers';
import { signIn, signInAsStaff, signInAsSubscriber } from 'codecrafters-frontend/tests/support/authentication-helpers';
import percySnapshot from '@percy/ember';

module('Acceptance | settings-page | profile-test', function (hooks) {
setupApplicationTest(hooks);
Expand Down Expand Up @@ -49,6 +50,18 @@ module('Acceptance | settings-page | profile-test', function (hooks) {
assert.strictEqual(userPage.githubDetails.link, 'https://github.com/rohitpaulk');
});

test('can enable dark mode', async function (assert) {
testScenario(this.server);
const user = signInAsStaff(this.owner, this.server);
user.update({ isVip: true });

await profilePage.visit();
await profilePage.darkModeToggle.clickOnOption('dark');

await percySnapshot('Settings Page - Dark Mode');
assert.strictEqual(1, 1); // TODO: Add more?
});

test('can refresh github username', async function (assert) {
testScenario(this.server);
signInAsSubscriber(this.owner, this.server);
Expand Down
26 changes: 0 additions & 26 deletions tests/integration/components/dark-mode-toggle-test.ts

This file was deleted.

Loading

0 comments on commit 77ba495

Please sign in to comment.