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: support multiple themes #64

Merged
merged 5 commits into from
Jan 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ deploy_review_b2b:
-e LOGGING=true
-e SENTRY_DSN=${SENTRY_DSN}
-e ICM_BASE_URL=${ICM_BASE_URL}
-e THEME=blue
-e ICM_CHANNEL=inSPIRED-inTRONICS_Business-Site
-e FEATURES=quoting,recently,compare,businessCustomerRegistration,advancedVariationHandling,sentry
--add-host $ICM_HOST:$ICM_IP
Expand Down
2 changes: 1 addition & 1 deletion CUSTOMIZING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ When modifying components it is most likely that related test cases will fail. I

## Styling

Changing the styling of **existing components** can be done by changing relevant information in the global style files under `src/theme`. If too many changes have to be made, it is better to **add the styling in additional files on global or component level**.
Changing the styling of **existing components** can be done by changing relevant information in the global style files under `src/styles`. If too many changes have to be made, it is better to **add the styling in additional files on global or component level**.

When styling on component level, all styling is encapsulated to exactly this component (default behavior). You can re-use variables from global styling on component level by adding imports like `@import '~theme/variables.scss';`.

Expand Down
14 changes: 13 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,24 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"extractCss": true,
"assets": [
"src/favicon.ico",
"src/assets",
"src/manifest.webmanifest"
],
"styles": ["src/styles.scss"],
"styles": [
{
"input": "src/styles/themes/default/style.scss",
"lazy": true,
"bundleName": "default"
},
{
"input": "src/styles/themes/blue/style.scss",
"lazy": true,
"bundleName": "blue"
}
],
"scripts": []
},
"configurations": {
Expand Down
1 change: 1 addition & 0 deletions nginx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Setup at least one PWA channel configuration:
- use optional `PWA_X_APPLICATION` for the application name
- use optional `PWA_X_LANG` for the default locale in the form of `lang_COUNTRY`
- use optional `PWA_X_FEATURES` for a comma separated list of active feature toggles
- use optional `PWA_X_THEME` for setting the theme of the channel

Temper with the default Page Speed configuration:

Expand Down
2 changes: 1 addition & 1 deletion nginx/channel.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ server {
if (-f /etc/nginx/conf.d/icm.conf) {
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;icmScheme=$scheme;icmHost=$http_host";
}
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;channel=$CHANNEL;application=$APPLICATION;features=$FEATURES" break;
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;channel=$CHANNEL;application=$APPLICATION;features=$FEATURES;theme=$THEME" break;

proxy_pass $UPSTREAM_PWA;
}
Expand Down
3 changes: 2 additions & 1 deletion nginx/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ do
eval "export APPLICATION=\${PWA_${i}_APPLICATION:-'-'}"
eval "export LANG=\${PWA_${i}_LANG:-'default'}"
eval "export FEATURES=\${PWA_${i}_FEATURES:-'default'}"
eval "export THEME=\${PWA_${i}_THEME:-''}"

echo "$i SUBDOMAIN=$SUBDOMAIN CHANNEL=$CHANNEL APPLICATION=$APPLICATION LANG=$LANG FEATURES=$FEATURES"

envsubst '$UPSTREAM_PWA,$SUBDOMAIN,$CHANNEL,$APPLICATION,$LANG,$FEATURES,$ICM_INCLUDE' </etc/nginx/conf.d/channel.conf.tmpl >/etc/nginx/conf.d/channel$i.conf
envsubst '$UPSTREAM_PWA,$SUBDOMAIN,$CHANNEL,$APPLICATION,$LANG,$FEATURES,$THEME,$ICM_INCLUDE' </etc/nginx/conf.d/channel.conf.tmpl >/etc/nginx/conf.d/channel$i.conf

i=$((i+1))
done
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"angular2-uuid": "^1.1.1",
"angulartics2": "^8.1.0",
"b64u": "^2.0.0",
"bootstrap": "^4.3.1",
"bootstrap": "^4.4.1",
"conventional-changelog-cli": "^2.0.21",
"core-js": "^2.6.9",
"express": "^4.17.1",
Expand Down
5 changes: 4 additions & 1 deletion src/app/core/configuration.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { environment } from '../../environments/environment';

import * as injectionKeys from './configurations/injection-keys';
import { FeatureToggleModule } from './feature-toggle.module';
import { ThemeService } from './utils/theme/theme.service';

@NgModule({
imports: [FeatureToggleModule],
Expand All @@ -34,10 +35,12 @@ import { FeatureToggleModule } from './feature-toggle.module';
{ provide: injectionKeys.MEDIUM_BREAKPOINT_WIDTH, useValue: environment.mediumBreakpointWidth },
{ provide: injectionKeys.LARGE_BREAKPOINT_WIDTH, useValue: environment.largeBreakpointWidth },
{ provide: injectionKeys.EXTRALARGE_BREAKPOINT_WIDTH, useValue: environment.extralargeBreakpointWidth },
{ provide: injectionKeys.THEME, useValue: environment.theme },
],
})
export class ConfigurationModule {
constructor(@Inject(LOCALE_ID) lang: string, translateService: TranslateService) {
constructor(@Inject(LOCALE_ID) lang: string, translateService: TranslateService, themeService: ThemeService) {
themeService.init();
registerLocaleData(localeDe);
registerLocaleData(localeFr);

Expand Down
5 changes: 5 additions & 0 deletions src/app/core/configurations/injection-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ export const EXTRALARGE_BREAKPOINT_WIDTH = new InjectionToken<number>('extralarg
* The captcha configuration siteKey
*/
export const CAPTCHA_SITE_KEY = new InjectionToken<string>('captchaSiteKey');

/**
* The configured theme for the application (or 'default' if not configured)
*/
export const THEME = new InjectionToken<string>('theme');
11 changes: 8 additions & 3 deletions src/app/core/store/configuration/configuration.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ export class ConfigurationEffects {
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_APPLICATION', 'icmApplication'),
this.stateProperties
.getStateOrEnvOrDefault<string | string[]>('FEATURES', 'features')
.pipe(map(x => (typeof x === 'string' ? x.split(/,/g) : x)))
.pipe(map(x => (typeof x === 'string' ? x.split(/,/g) : x))),
this.stateProperties.getStateOrEnvOrDefault<string>('THEME', 'theme').pipe(map(x => x || 'default'))
),
map(
([, baseURL, server, serverStatic, channel, application, features]) =>
new ApplyConfiguration({ baseURL, server, serverStatic, channel, application, features })
([, baseURL, server, serverStatic, channel, application, features, theme]) =>
new ApplyConfiguration({ baseURL, server, serverStatic, channel, application, features, theme })
)
);

Expand Down Expand Up @@ -87,6 +88,10 @@ export class ConfigurationEffects {
}
}

if (paramMap.has('theme')) {
properties.theme = paramMap.get('theme');
}

return Object.keys(properties).length ? [new ApplyConfiguration(properties)] : [];
}

Expand Down
2 changes: 2 additions & 0 deletions src/app/core/store/configuration/configuration.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface ConfigurationState {
application?: string;
features?: string[];
gtmToken?: string;
theme?: string;
}

const initialState: ConfigurationState = {
Expand All @@ -18,6 +19,7 @@ const initialState: ConfigurationState = {
application: undefined,
features: [],
gtmToken: undefined,
theme: undefined,
};

export function configurationReducer(state = initialState, action: ConfigurationAction): ConfigurationState {
Expand Down
5 changes: 5 additions & 0 deletions src/app/core/store/configuration/configuration.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,8 @@ export const getGTMToken = createSelector(
getConfigurationState,
state => state.gtmToken
);

export const getTheme = createSelector(
getConfigurationState,
state => state.theme
);
56 changes: 56 additions & 0 deletions src/app/core/utils/theme/theme.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { distinctUntilChanged, filter } from 'rxjs/operators';

import { getTheme } from 'ish-core/store/configuration';

/**
* Service to add the configured/selected theme’s CSS file in the HTML’s head.
*
* See: "Angular: Multiple Themes Without Killing Bundle Size (With Material or Not)" by @Kmathy15
* https://medium.com/better-programming/angular-multiple-themes-without-killing-bundle-size-with-material-or-not-5a80849b6b34
*/
@Injectable({ providedIn: 'root' })
export class ThemeService {
private renderer: Renderer2;
private head: HTMLElement;
private themeLinks: HTMLElement[] = [];

constructor(
private rendererFactory: RendererFactory2,
@Inject(DOCUMENT) private document: Document,
private store: Store<{}>
) {}

init() {
this.head = this.document.head;
this.renderer = this.rendererFactory.createRenderer(undefined, undefined);
this.store
.pipe(select(getTheme))
.pipe(
distinctUntilChanged(),
filter(x => !!x)
)
.subscribe(async theme => {
await this.loadCss(`${theme}.css`);

// remove style of previous theme
if (this.themeLinks.length === 2) {
this.renderer.removeChild(this.head, this.themeLinks.shift());
}
});
}

private async loadCss(filename: string) {
return new Promise(resolve => {
const linkEl: HTMLElement = this.renderer.createElement('link');
this.renderer.setAttribute(linkEl, 'rel', 'stylesheet');
this.renderer.setAttribute(linkEl, 'type', 'text/css');
this.renderer.setAttribute(linkEl, 'href', filename);
this.renderer.setProperty(linkEl, 'onload', resolve);
this.renderer.appendChild(this.head, linkEl);
this.themeLinks = [...this.themeLinks, linkEl];
});
}
}
32 changes: 12 additions & 20 deletions src/app/shell/header/header-checkout/header-checkout.component.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
<div class="top-header header-checkout">
<div class="row">
<div class="container">
<div class="header-utility col-3 col-md-12">
<ul class="user-links">
<li><ish-login-status [logoutOnly]="true"></ish-login-status></li>
</ul>
</div>
<div class="mid-header col-9 col-md-12">
<div class="logo-wrapper d-md-none">
<a rel="home" routerLink="/home" class="mobile-logo" data-testing-id="link-home">
<img src="assets/img/logo_mobile.png" alt="Logo" width="41" height="41" />
</a>
</div>
<div class="logo-wrapper d-none d-md-block">
<a rel="home" routerLink="/home" data-testing-id="link-home">
<img src="assets/img/logo.png" alt="Logo" />
</a>
</div>
</div>
<div class="header header-checkout container">
<div class="header-utility col-3 col-md-12">
<ul class="user-links">
<li><ish-login-status [logoutOnly]="true"></ish-login-status></li>
</ul>
</div>
<div class="mid-header col-9 col-md-12">
<div class="logo-wrapper d-md-none">
<a rel="home" routerLink="/home" class="mobile-logo" data-testing-id="link-home"></a>
</div>
<div class="logo-wrapper d-none d-md-block">
<a rel="home" routerLink="/home" class="logo" data-testing-id="link-home"></a>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,14 @@
routerLink="/home"
class="logo"
data-testing-id="header-home-link-desktop"
>
<img src="assets/img/logo.png" alt="Logo" />
</a>
></a>
<a
*ngIf="showMobileLogoLink"
rel="home"
routerLink="/home"
class="mobile-logo"
data-testing-id="header-home-link-mobile"
><img src="assets/img/logo_mobile.png" alt="Logo" width="41" height="41"
/></a>
></a>
</div>

<button class="navbar-toggler" type="button" (click)="toggle('navbar')">
Expand Down
21 changes: 6 additions & 15 deletions src/app/shell/header/header-simple/header-simple.component.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
<div class="top-header">
<nav class="container">
<div class="row">
<a rel="home" routerLink="/home" class="d-md-none mobile-logo">
<img src="assets/img/logo_mobile.png" alt="Logo" width="41" height="41" />
</a>
<div class="header header-simple container">
<div class="mid-header">
<div class="logo-wrapper d-none d-md-block">
<a rel="home" routerLink="/home" class="logo" data-testing-id="link-home"></a>
</div>
</nav>
</div>

<div class="mid-header">
<div class="container">
<div class="row">
<div class="col-md-3 logo-wrapper d-none d-md-block">
<a rel="home" routerLink="/home"> <img src="assets/img/logo.png" alt="Logo" /> </a>
</div>
<div class="logo-wrapper d-md-none">
<a rel="home" routerLink="/home" class="mobile-logo" data-testing-id="link-home"></a>
</div>
</div>
</div>
Binary file added src/assets/img/themes/blue/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/img/themes/blue/logo_mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions src/environments/environment.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Locale } from 'ish-core/models/locale/locale.model';
import { ViewType } from 'ish-core/models/viewtype/viewtype.types';

export interface Environment {
production: boolean;

Expand Down Expand Up @@ -63,11 +66,14 @@ export interface Environment {
productListingItemsPerPage: number;

// default viewType used for product listings
defaultProductListingViewType: 'grid' | 'list';
defaultProductListingViewType: ViewType;

// enable or disable service worker
serviceWorker: boolean;

// configuration of the available locales - hard coded for now
locales: { lang: string; currency: string; value: string; displayName: string; displayLong: string }[];
locales: Locale[];

// configuration of the styling theme ('default' if not configured)
theme?: string;
}
25 changes: 0 additions & 25 deletions src/styles.scss

This file was deleted.

6 changes: 5 additions & 1 deletion src/theme/breakpoints.scss → src/styles/breakpoints.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// bootstrap 3 breakpoints
///////////////////////////////////////////////////////////////////////////////
//
// breakpoint wrapper to match older bootstrap 3 breakpoints
//
///////////////////////////////////////////////////////////////////////////////

// Extra small screen / phone
$screen-xs: map-get($grid-breakpoints, 'xs');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
padding-top: 0;
padding-bottom: 0;

.logo img {
.logo {
height: 25px;
padding-right: $space-default * 2;
}
Expand Down
Loading