Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): allow change top bar theme color #3128

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/flipt.schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ import "strings"
#ui: {
enabled?: bool | *true
default_theme?: "light" | "dark" | *"system"
topbar?: {
color?: string
label?: string
}
}

#audit: {
Expand Down
12 changes: 12 additions & 0 deletions config/flipt.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,18 @@
"enum": ["light", "dark", "system"],
"default": "system",
"deprecated": false
},
"topbar": {
"type": "object",
"additionalProperties": false,
"properties": {
"color": {
"type": "string"
},
"label": {
"type": "string"
}
}
}
},
"title": "UI"
Expand Down
10 changes: 10 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,16 @@ func TestLoad(t *testing.T) {
path: "./testdata/analytics/invalid_buffer_configuration_flush_period.yml",
wantErr: errors.New("flush period below 10 seconds"),
},
{
name: "ui topbar with custom color",
path: "./testdata/ui/topbar_color.yml",
expected: func() *Config {
cfg := Default()
cfg.UI.Topbar.Color = "#42bda0"
cfg.UI.Topbar.Label = "World"
return cfg
},
},
}

for _, tt := range tests {
Expand Down
5 changes: 5 additions & 0 deletions internal/config/testdata/ui/topbar_color.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ui:
topbar:
color: "#42bda0"
label: World

9 changes: 8 additions & 1 deletion internal/config/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ var (
_ defaulter = (*UIConfig)(nil)
)

// UITopbar represents the configuration of a user interface top bar component.
type UITopbar struct {
Color string `json:"color" mapstructure:"color" yaml:"color"`
Label string `json:"label" mapstructure:"label" yaml:"label"`
}

// UIConfig contains fields, which control the behaviour
// of Flipt's user interface.
type UIConfig struct {
DefaultTheme UITheme `json:"defaultTheme" mapstructure:"default_theme" yaml:"default_theme"`
DefaultTheme UITheme `json:"defaultTheme" mapstructure:"default_theme" yaml:"default_theme"`
Topbar UITopbar `json:"topbar,omitempty" mapstructure:"topbar" yaml:"topbar,omitempty"`
}

func (c *UIConfig) setDefaults(v *viper.Viper) error {
Expand Down
13 changes: 12 additions & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import formbricks from '@formbricks/js';
import { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
import { useSelector } from 'react-redux';
import { selectConfig } from '~/app/meta/metaSlice';
import { selectCurrentNamespace } from '~/app/namespaces/namespacesSlice';
import { createHashRouter, redirect, RouterProvider } from 'react-router-dom';
import ErrorLayout from './app/ErrorLayout';
import Flag from './app/flags/Flag';
Expand Down Expand Up @@ -174,11 +176,20 @@ export default function App() {
}
}, [theme, systemPrefersDark]);

const { ui } = useSelector(selectConfig);

const namespace = useSelector(selectCurrentNamespace);

let title = `Flipt · ${namespace.key}`;
if (ui.topbar?.label) {
title = `Flipt/${ui.topbar.label} · ${namespace.key}`;
}

return (
<>
<Helmet>
<meta charSet="utf-8" />
<title>Flipt</title>
<title>{title}</title>
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script dangerouslySetInnerHTML={{ __html: nightwind.init() }} />
Expand Down
3 changes: 2 additions & 1 deletion ui/src/app/meta/metaSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const initialState: IMetaSlice = {
readOnly: false
},
ui: {
defaultTheme: Theme.SYSTEM
defaultTheme: Theme.SYSTEM,
topbar: { color: '' }
},
analyticsEnabled: false
}
Expand Down
13 changes: 9 additions & 4 deletions ui/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Bars3BottomLeftIcon } from '@heroicons/react/24/outline';
import { useSelector } from 'react-redux';
import { selectInfo, selectReadonly } from '~/app/meta/metaSlice';
import { selectConfig, selectInfo, selectReadonly } from '~/app/meta/metaSlice';
import { useSession } from '~/data/hooks/session';
import Notifications from './header/Notifications';
import ReadOnly from './header/ReadOnly';
import UserProfile from './header/UserProfile';

type HeaderProps = {
setSidebarOpen: (sidebarOpen: boolean) => void;
};
Expand All @@ -17,19 +16,25 @@ export default function Header(props: HeaderProps) {
const readOnly = useSelector(selectReadonly);

const { session } = useSession();
const { ui } = useSelector(selectConfig);
const topbarStyle = { backgroundColor: ui.topbar?.color };

return (
<div className="sticky top-0 z-20 flex h-16 flex-shrink-0 bg-gray-950 dark:border-b dark:border-b-white/20">
<div className="sticky top-0 z-20 flex h-16 flex-shrink-0 bg-gray-950 dark:border-b dark:border-b-white/20 dark:md:border-b-0">
<button
type="button"
className="without-ring nightwind-prevent text-white px-4 md:hidden"
style={topbarStyle}
onClick={() => setSidebarOpen(true)}
>
<span className="sr-only">Open sidebar</span>
<Bars3BottomLeftIcon className="h-6 w-6" aria-hidden="true" />
</button>

<div className="flex flex-1 justify-between px-4">
<div
className="top-0 flex flex-1 justify-between px-4"
style={topbarStyle}
>
<div className="flex flex-1" />
<div className="flex items-center">
{/* read-only mode */}
Expand Down
9 changes: 8 additions & 1 deletion ui/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Fragment } from 'react';
import { Link } from 'react-router-dom';
import logoLight from '~/assets/logo-light.png';
import Nav from './Nav';
import { useSelector } from 'react-redux';
import { selectConfig } from '~/app/meta/metaSlice';

type SidebarProps = {
sidebarOpen: boolean;
Expand All @@ -13,6 +15,8 @@ type SidebarProps = {
export default function Sidebar(props: SidebarProps) {
const { sidebarOpen, setSidebarOpen } = props;

const { ui } = useSelector(selectConfig);
const topbarStyle = { backgroundColor: ui.topbar?.color };
return (
<>
<Transition.Root show={sidebarOpen} as={Fragment}>
Expand Down Expand Up @@ -94,7 +98,10 @@ export default function Sidebar(props: SidebarProps) {
{/* Static sidebar for desktop */}
<div className="hidden md:fixed md:inset-y-0 md:flex md:w-64 md:flex-col">
<div className="bg-gray-200 flex min-h-0 flex-1 flex-col">
<div className="relative flex h-16 flex-shrink-0 items-center bg-gray-950 px-4 pb-1 pt-2 dark:border-b dark:border-b-white/20">
<div
className="relative flex h-16 flex-shrink-0 items-center bg-gray-950 px-4 pb-1 pt-2 dark:border-b dark:border-b-white/20"
style={topbarStyle}
>
<Link to="/">
<img
src={logoLight}
Expand Down
6 changes: 6 additions & 0 deletions ui/src/types/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ export interface IStorage {
git?: IGit;
}

export interface ITopbar {
color?: string;
label?: string;
}

export interface IUI {
defaultTheme: Theme;
topbar: ITopbar;
}

export interface IConfig {
Expand Down
Loading