Skip to content

Commit

Permalink
Merge pull request novuhq#418 from notifirehq/NV-270-Widget-design-im…
Browse files Browse the repository at this point in the history
…plementation

Widget - New Design
  • Loading branch information
ainouzgali authored Mar 30, 2022
2 parents 535548a + 6f97836 commit 89f5c0c
Show file tree
Hide file tree
Showing 20 changed files with 396 additions and 192 deletions.
15 changes: 0 additions & 15 deletions apps/web/cypress/tests/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,6 @@ describe('Settings Screen', function () {
cy.get('div[aria-valuetext="rgba(185, 103, 199, 1)"]');
cy.get('body').click();

/* cy.getByTestId('font-color-picker').click({ force: true });
cy.get('button[aria-label="#37D67A"]').click({ force: true });
cy.getByTestId('font-color-picker').click({ force: true });
cy.get('div[aria-valuetext="rgba(56, 214, 122, 1)"]');
cy.get('body').click();
*/

cy.getByTestId('content-background-picker').click({ force: true });
cy.get('button[aria-label="#2CCCE4"]').click({ force: true });
cy.getByTestId('content-background-picker').click({ force: true });
cy.get('div[aria-valuetext="rgba(43, 202, 227, 1)"]');
cy.get('body').click();

cy.getByTestId('font-family-selector').click({ force: true });
cy.get('.mantine-Select-dropdown .mantine-Select-item').contains('Lato').click();
cy.getByTestId('font-family-selector').should('have.value', 'Lato');
Expand All @@ -71,8 +58,6 @@ describe('Settings Screen', function () {

cy.reload();
cy.getByTestId('color-picker').should('have.value', '#b967c7');
/* cy.getByTestId('font-color-picker').should('have.value', '#0eb554'); */
cy.getByTestId('content-background-picker').should('have.value', '#2bcae3');
cy.getByTestId('font-family-selector').should('have.value', 'Lato');
});
});
72 changes: 18 additions & 54 deletions apps/web/src/pages/settings/components/BrandingForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { useForm, Controller } from 'react-hook-form';
import { Dropzone, DropzoneStatus } from '@mantine/dropzone';
import { useMutation } from 'react-query';
import axios from 'axios';
import { showNotification } from '@mantine/notifications';
import { useMantineTheme, Group, InputWrapper, LoadingOverlay } from '@mantine/core';
import { Button, colors, Select, ColorInput } from '../../../design-system';
import { getSignedUrl } from '../../../api/storage';
import { updateBrandingSettings } from '../../../api/application';
import { inputStyles } from '../../../design-system/config/inputs.styles';
import { Upload } from '../../../design-system/icons';
import Card from '../../../components/layout/components/Card';
import { showNotification } from '@mantine/notifications';

const mimeTypes = {
'image/jpeg': 'jpeg',
Expand Down Expand Up @@ -48,12 +48,6 @@ export function BrandingForm({
if (application.branding?.color) {
setValue('color', application?.branding?.color);
}
if (application.branding?.fontColor) {
setValue('fontColor', application?.branding?.fontColor);
}
if (application.branding?.contentBackground) {
setValue('contentBackground', application?.branding?.contentBackground);
}
if (application.branding?.fontFamily) {
setValue('fontFamily', application?.branding?.fontFamily);
}
Expand Down Expand Up @@ -95,12 +89,10 @@ export function BrandingForm({
setImageLoading(false);
}

async function saveBrandsForm({ color, fontColor, contentBackground, fontFamily }) {
async function saveBrandsForm({ color, fontFamily }) {
const brandData = {
color,
logo: image,
fontColor,
contentBackground,
fontFamily,
};

Expand All @@ -115,8 +107,6 @@ export function BrandingForm({
const { setValue, handleSubmit, control } = useForm({
defaultValues: {
fontFamily: application?.branding?.fontFamily || 'Roboto',
fontColor: application?.branding?.fontColor || '#333737',
contentBackground: application?.branding?.contentBackground || '#efefef',
color: application?.branding?.color || '#f47373',
image: image || '',
file: file || '',
Expand All @@ -129,48 +119,6 @@ export function BrandingForm({
<LoadingOverlay visible={isLoading} />
<form onSubmit={handleSubmit(saveBrandsForm)}>
<Group grow spacing={50} mt={0} align="flex-start">
<Card title="In-App Widget Customizations">
<Controller
render={({ field }) => (
<Select
label="Font Family"
description="Will be used as the main font-family in the in-app widget"
placeholder="Select a font family"
data={['Roboto', 'Montserrat', 'Open Sans', 'Lato', 'Nunito', 'Oswald', 'Raleway']}
data-test-id="font-family-selector"
{...field}
/>
)}
control={control}
name="fontFamily"
/>
<Controller
render={({ field }) => (
<ColorInput
mt={25}
label="Font Color"
description="Will be used for text in the in-app widget"
data-test-id="font-color-picker"
{...field}
/>
)}
control={control}
name="fontColor"
/>
<Controller
render={({ field }) => (
<ColorInput
mt={25}
label="Content Background Color"
description="Will be used as the background color for the inner content of the in-app widget"
data-test-id="content-background-picker"
{...field}
/>
)}
control={control}
name="contentBackground"
/>
</Card>
<Card title="Brand Setting">
<Controller
render={({ field }) => (
Expand Down Expand Up @@ -215,6 +163,22 @@ export function BrandingForm({
name="color"
/>
</Card>
<Card title="In-App Widget Customizations">
<Controller
render={({ field }) => (
<Select
label="Font Family"
description="Will be used as the main font-family in the in-app widget"
placeholder="Select a font family"
data={['Roboto', 'Montserrat', 'Open Sans', 'Lato', 'Nunito', 'Oswald', 'Raleway']}
data-test-id="font-family-selector"
{...field}
/>
)}
control={control}
name="fontFamily"
/>
</Card>
</Group>
<Button submit mb={20} mt={25} loading={isUpdateBrandingLoading} data-test-id="submit-branding-settings">
Update
Expand Down
8 changes: 0 additions & 8 deletions apps/widget/craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,4 @@ module.exports = {
],
],
},
plugins: [
{
plugin: CracoAntDesignPlugin,
options: {
customizeThemeLessPath: path.join(__dirname, 'src/styles/theme.less'),
},
},
],
};
6 changes: 3 additions & 3 deletions apps/widget/cypress/integration/branding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ describe('App Branding', function () {
});
});

/**
* For now, user's branding will only include font family, layout direction and brand color.
*/
it('change main theme color', function () {
cy.getByTestId('notification-list-item').then(($els) => {
// get Window reference from element
Expand All @@ -28,9 +31,6 @@ describe('App Branding', function () {
expect(contentValue).to.eq('rgb(42, 157, 143)');
});

// colors convert to rgb for some reason
cy.getByTestId('notification-list-item').first().should('have.css', 'color', 'rgb(33, 78, 73)');
cy.getByTestId('main-wrapper').first().should('have.css', 'background-color', 'rgb(194, 203, 210)');
cy.get('body').first().should('have.css', 'font-family', 'Montserrat, Helvetica, sans-serif');
});
});
4 changes: 3 additions & 1 deletion apps/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@ant-design/icons": "^4.5.0",
"@mantine/core": "^4.1.0",
"@notifire/sdk": "^0.3.4",
"@notifire/shared": "^0.3.5",
"@testing-library/jest-dom": "^5.11.4",
Expand All @@ -16,14 +17,15 @@
"antd": "^4.10.0",
"axios": "^0.21.1",
"babel-plugin-import": "^1.13.3",
"chroma-js": "^2.4.2",
"eslint-plugin-cypress": "^2.12.1",
"iframe-resizer": "^4.3.1",
"jwt-decode": "^3.1.2",
"moment": "^2.29.1",
"polished": "^4.1.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-infinite-scroll-component": "^6.0.0",
"eslint-plugin-cypress": "^2.12.1",
"react-infinite-scroller": "^1.2.4",
"react-query": "^3.12.1",
"react-refresh": "^0.11.0",
Expand Down
Binary file added apps/widget/public/no-new-notifications.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 9 additions & 8 deletions apps/widget/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import styled, { createGlobalStyle, ThemeProvider } from 'styled-components';
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
Expand All @@ -16,6 +16,7 @@ import { SocketContext } from './store/socket/socket.store';
import { WidgetShell } from './ApplicationShell';
import { getApplication } from './api/application';
import { useAuth } from './hooks/use-auth.hook';
import { colors } from './shared/config/colors';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -61,6 +62,7 @@ const GlobalStyle = createGlobalStyle<{ fontFamily: string }>`
function AppContent() {
const { isLoggedIn } = useAuth();
const { socket } = useSocketController();
const [userColorScheme, setUserColorScheme] = useState<'light' | 'dark'>('light');
const { data: application } = useQuery<Pick<IApplication, '_id' | 'name' | 'branding'>>(
'application',
getApplication,
Expand All @@ -71,14 +73,15 @@ function AppContent() {

const theme = {
colors: {
main: application?.branding?.color || '#cd5450',
fontColor: application?.branding?.fontColor || '#333737',
contentBackground: application?.branding?.contentBackground || '#efefef',
main: application?.branding?.color || colors.vertical,
fontColor: userColorScheme === 'light' ? colors.B40 : colors.white,
secondaryFontColor: userColorScheme === 'light' ? colors.B80 : colors.B40,
},
fontFamily: application?.branding?.fontFamily || 'Roboto',
fontFamily: application?.branding?.fontFamily || 'Lato',
layout: {
direction: (application?.branding?.direction === 'rtl' ? 'rtl' : 'ltr') as 'ltr' | 'rtl',
},
colorScheme: userColorScheme,
};

useEffect(() => {
Expand Down Expand Up @@ -118,12 +121,10 @@ function AppContent() {
}

const Wrap = styled.div<{ layoutDirection: 'ltr' | 'rtl'; brandColor: string; fontColor: string }>`
border-radius: 3px;
overflow: hidden;
border: 1px solid #e9e9e9;
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 7px 0px;
direction: ${({ layoutDirection }) => layoutDirection};
color: ${({ fontColor }) => fontColor};
min-width: 420px;
::-moz-selection {
background: ${({ brandColor }) => brandColor};
Expand Down
45 changes: 28 additions & 17 deletions apps/widget/src/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from 'react-query';
import { useInfiniteQuery, useMutation } from 'react-query';
import { ChannelCTATypeEnum, IMessage } from '@notifire/shared';
import styled from 'styled-components';
import { NotificationsList } from './components/NotificationsList';
Expand All @@ -10,13 +10,13 @@ import { postUsageLog } from './api/usage';

export function Main() {
const [currentPage, setCurrentPage] = useState<number>(0);
const { data, fetchNextPage, isFetchingNextPage, hasNextPage, isFetched, refetch, isFetching } = useInfiniteQuery<
IMessage[]
>('notifications-feed', async ({ pageParam = 0 }) => getNotificationsList(pageParam), {
getNextPageParam: (lastPage) => {
return lastPage.length === 10 ? currentPage + 1 : undefined;
},
});

const { isLoading, data, fetchNextPage, isFetchingNextPage, hasNextPage, isFetched, refetch, isFetching } =
useInfiniteQuery<IMessage[]>('notifications-feed', async ({ pageParam = 0 }) => getNotificationsList(pageParam), {
getNextPageParam: (lastPage) => {
return lastPage.length === 10 ? currentPage + 1 : undefined;
},
});
const { mutateAsync: markNotificationAsSeen } = useMutation<{ body: IMessage }, never, { messageId: string }>(
(params) => markMessageAsSeen(params.messageId)
);
Expand Down Expand Up @@ -67,16 +67,27 @@ export function Main() {

return (
<MainWrapper data-test-id="main-wrapper">
<NotificationsList
onNotificationClicked={onNotificationClicked}
notifications={data?.pages || []}
onFetch={fetchNext}
hasNextPage={!isFetched || (hasNextPage as boolean)}
/>
{!isLoading && isFetched && !isFetching && data?.pages[0].length === 0 ? (
<div
style={{
textAlign: 'center',
minHeight: 350,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<img src="/no-new-notifications.png" alt="logo" style={{ maxWidth: 200 }} />
</div>
) : (
<NotificationsList
onNotificationClicked={onNotificationClicked}
notifications={data?.pages || []}
onFetch={fetchNext}
hasNextPage={!isFetched || (hasNextPage as boolean)}
/>
)}
</MainWrapper>
);
}

const MainWrapper = styled.div`
background: ${({ theme }) => theme.colors.contentBackground};
`;
const MainWrapper = styled.div``;
28 changes: 28 additions & 0 deletions apps/widget/src/components/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Loader as MantineLoader } from '@mantine/core';
import { useTheme } from 'styled-components';
import chroma from 'chroma-js';
import { getLinearGradientColorStopValues } from '../shared/utils/getLinearGradientColorStopValues';

export const Loader = ({ color }: { color?: string }) => {
const theme: any = useTheme();
const loaderColor = color || theme.colors.main;

return (
<div
style={{
textAlign: 'center',
minHeight: 300,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<MantineLoader
color={
loaderColor.indexOf('gradient') === -1
? loaderColor
: chroma.average(getLinearGradientColorStopValues(loaderColor))
}
/>
</div>
);
};
Loading

0 comments on commit 89f5c0c

Please sign in to comment.