Skip to content

Commit

Permalink
feat: add alerts to authenticate & collection page (#76)
Browse files Browse the repository at this point in the history
* feat: added alerts to authenticate page

* feat: added alerts to collections page

* build: fixed missing import

* test: fixed jest config

* fix: improved styling of alerts

* test: added basic tests for showing alerts

Co-authored-by: Wouter Janssens <wouter@digita.ai>
  • Loading branch information
lem-onade and wouteraj authored Apr 29, 2021
1 parent bc34d8f commit 7531abe
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 92 deletions.
5 changes: 4 additions & 1 deletion packages/nde-erfgoed-components/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const config: Config.InitialOptions = {
},
},
},
transform: {
'^.+\\.ts?$': 'ts-jest'
},
};

export default config;
export default config;
29 changes: 16 additions & 13 deletions packages/nde-erfgoed-core/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import type {Config} from '@jest/types';

const config: Config.InitialOptions = {
preset: 'ts-jest/presets/js-with-ts',
testMatch: ['**/+(*.)+(spec).+(ts)'],
transformIgnorePatterns: ['node_modules/(?!(lit-element|lit-html|rx-lit)/)'],
moduleNameMapper: {
'~(.*)': '<rootDir>/lib/$1',
},
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
diagnostics: {
warnOnly: true,
preset: 'ts-jest/presets/js-with-ts',
testMatch: ['**/+(*.)+(spec).+(ts)'],
transformIgnorePatterns: ['node_modules/(?!(lit-element|lit-html|rx-lit)/)'],
moduleNameMapper: {
'~(.*)': '<rootDir>/lib/$1',
},
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
diagnostics: {
warnOnly: true,
},
},
},
},
transform: {
'^.+\\.ts?$': 'ts-jest'
},
};

export default config;
export default config;
5 changes: 4 additions & 1 deletion packages/nde-erfgoed-manage/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const config: Config.InitialOptions = {
},
},
},
transform: {
'^.+\\.ts?$': 'ts-jest'
},
};

export default config;
export default config;
27 changes: 0 additions & 27 deletions packages/nde-erfgoed-manage/lib/app.root.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,4 @@ describe('AppRootComponent', () => {
it('should be correctly instantiated', () => {
expect(component).toBeTruthy();
});

it('should show alerts when set', async () => {
component.alerts = [ {
type: 'success',
message: 'Foo',
} ];

window.document.body.appendChild(component);
await component.updateComplete;

const alerts = window.document.body.getElementsByTagName('nde-app-root')[0].shadowRoot.querySelectorAll('nde-alert');

expect(alerts).toBeTruthy();
expect(alerts.length).toBe(1);
});

it('should not show alerts when unset', async () => {
component.alerts = null;

window.document.body.appendChild(component);
await component.updateComplete;

const alerts = window.document.body.getElementsByTagName('nde-app-root')[0].shadowRoot.querySelectorAll('nde-alert');

expect(alerts).toBeTruthy();
expect(alerts.length).toBe(0);
});
});
20 changes: 1 addition & 19 deletions packages/nde-erfgoed-manage/lib/app.root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ import { CollectionsRootComponent } from './features/collections/collections-roo
*/
export class AppRootComponent extends RxLitElement {

/**
* The component's alerts.
*/
@property({type: Array})
public alerts: Alert[];

/**
* The component's logger.
*/
Expand Down Expand Up @@ -90,13 +84,6 @@ export class AppRootComponent extends RxLitElement {
this.subscribe('state', from(this.actor).pipe(
tap((state) => this.logger.debug(CollectionsRootComponent.name, 'AppState change:', {actor: this.actor, state})),
));

/**
* Subscribes property alerts to changes in the app context.
*/
this.subscribe('alerts', from(this.actor).pipe(
map((state) => state.context.alerts),
));
}

/**
Expand All @@ -105,13 +92,7 @@ export class AppRootComponent extends RxLitElement {
* @returns The rendered HTML of the component.
*/
render() {
// Create an alert components for each alert.
const alerts = this.alerts?.map((alert) => html`<nde-alert .logger='${this.logger}' .translator='${this.translator}' .alert='${alert}' @dismiss="${this.dismiss}"></nde-alert>`);

return html`
<h1>${this.translator.translate('nde.app.root.title')}</h1>
<button @click="${() => this.actor.send(AppEvents.LOGGED_OUT)}" ?hidden="${this.state?.matches({[AppRootStates.FEATURE]: AppFeatureStates.AUTHENTICATE})}">Logout</button>
${ alerts }
${ this.state?.matches({[AppRootStates.FEATURE]: AppFeatureStates.AUTHENTICATE}) && html`<nde-authenticate-root .actor='${this.actor.children.get(AppActors.AUTHENTICATE_MACHINE)}' .logger='${this.logger}' .translator='${this.translator}'></nde-authenticate-root>` }
${ this.state?.matches({[AppRootStates.FEATURE]: AppFeatureStates.COLLECTIONS}) && html`<nde-collections-root .actor='${this.actor.children.get(AppActors.COLLECTIONS_MACHINE)}' .logger='${this.logger}' .translator='${this.translator}'></nde-collections-root>` }
`;
Expand All @@ -126,6 +107,7 @@ export class AppRootComponent extends RxLitElement {
css`
:host {
height: 100%;
flex-direction: column;
display: flex;
background-color: var(--colors-primary-dark);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { FormActors } from '@digita-ai/nde-erfgoed-components';
import { ConsoleLogger, LoggerLevel, SolidMockService } from '@digita-ai/nde-erfgoed-core';
import { interpret, Interpreter } from 'xstate';
import { AuthenticateRootComponent } from './authenticate-root.component';
import { AuthenticateContext, authenticateMachine } from './authenticate.machine';

const solid = new SolidMockService(new ConsoleLogger(LoggerLevel.silly, LoggerLevel.silly));

describe('AuthenticateRootComponent', () => {
let component: AuthenticateRootComponent;
let machine: Interpreter<AuthenticateContext>;

beforeEach(() => {
machine = interpret(authenticateMachine(solid));
component = window.document.createElement('nde-authenticate-root') as AuthenticateRootComponent;

component.actor = machine;
component.formActor = machine.children.get(FormActors.FORM_MACHINE);
});

afterEach(() => {
document.getElementsByTagName('html')[0].innerHTML = '';
});

it('should be correctly instantiated', () => {
expect(component).toBeTruthy();
});

it('should show alerts when set', async () => {
component.alerts = [ {
type: 'success',
message: 'Foo',
} ];

window.document.body.appendChild(component);
await component.updateComplete;

const alerts = window.document.body.getElementsByTagName('nde-authenticate-root')[0].shadowRoot.querySelectorAll('nde-alert');

expect(alerts).toBeTruthy();
expect(alerts.length).toBe(1);
});

it('should not show alerts when unset', async () => {
component.alerts = null;

window.document.body.appendChild(component);
await component.updateComplete;

const alerts = window.document.body.getElementsByTagName('nde-authenticate-root')[0].shadowRoot.querySelectorAll('nde-alert');

expect(alerts).toBeTruthy();
expect(alerts.length).toBe(0);
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { html, property, PropertyValues, internalProperty, unsafeCSS, css } from 'lit-element';
import { Logger, Translator } from '@digita-ai/nde-erfgoed-core';
import { Event, FormActors, FormRootStates, FormSubmissionStates, FormCleanlinessStates, FormValidationStates, FormEvents } from '@digita-ai/nde-erfgoed-components';
import { ArgumentError, Logger, Translator } from '@digita-ai/nde-erfgoed-core';
import { Event, FormActors, FormRootStates, FormSubmissionStates, FormCleanlinessStates, FormValidationStates, FormEvents, Alert } from '@digita-ai/nde-erfgoed-components';
import { ActorRef, Interpreter, State} from 'xstate';
import { RxLitElement } from 'rx-lit';
import { unsafeSVG } from 'lit-html/directives/unsafe-svg';
import { Login, NdeLogoInverse, Theme } from '@digita-ai/nde-erfgoed-theme';
import { map } from 'rxjs/operators';
import { from } from 'rxjs';
import { AppEvents } from '../../app.events';
import { AuthenticateContext } from './authenticate.machine';

/**
Expand All @@ -32,6 +33,12 @@ export class AuthenticateRootComponent extends RxLitElement {
@property({type: Object})
public actor: Interpreter<AuthenticateContext>;

/**
* The component's alerts.
*/
@internalProperty()
alerts: Alert[];

/**
* The actor responsible for form validation in this component.
*/
Expand All @@ -51,8 +58,7 @@ export class AuthenticateRootComponent extends RxLitElement {
enableSubmit?: boolean;

/**
* Hook called on first update after connection to the DOM.
* It subscribes to the actor, logs state changes, and pipes state to the properties.
* Hook called on at every update after connection to the DOM.
*/
updated(changed: PropertyValues) {
super.updated(changed);
Expand All @@ -61,6 +67,11 @@ export class AuthenticateRootComponent extends RxLitElement {
this.subscribe('formActor', from(this.actor).pipe(
map((state) => state.children[FormActors.FORM_MACHINE]),
));

if(this.actor.parent) {
this.subscribe('alerts', from(this.actor.parent)
.pipe(map((state) => state.context.alerts)));
}
}

if(changed.has('formActor')){
Expand All @@ -75,28 +86,50 @@ export class AuthenticateRootComponent extends RxLitElement {
}
}

/**
* Handles a dismiss event by sending a dismiss alert event to its parent.
*
* @param event Dismiss event dispatched by an alert componet.
*/
handleDismiss(event: CustomEvent<Alert>) {
if (!event || !event.detail) {
throw new ArgumentError('Argument event || event.detail should be set.', event && event.detail);
}

if (!this.actor || !this.actor.parent) {
throw new ArgumentError('Argument this.actor || !this.actor.parent should be set.', this.actor || !this.actor.parent);
}

this.actor.parent.send(AppEvents.DISMISS_ALERT, { alert: event.detail });
}

/**
* Renders the component as HTML.
*
* @returns The rendered HTML of the component.
*/
render() {
return this.formActor ? html`
// Create an alert components for each alert.
const alerts = this.alerts?.map((alert) => html`<nde-alert .logger='${this.logger}' .translator='${this.translator}' .alert='${alert}' @dismiss="${this.handleDismiss}"></nde-alert>`);

return html`
<div class="title-container">
${ unsafeSVG(NdeLogoInverse) }
<h1>${this.translator.translate('nde.features.authenticate.pages.login.title')}</h1>
<h1>${this.translator?.translate('nde.features.authenticate.pages.login.title')}</h1>
</div>
<div class="form-container">
${ alerts }
${ this.formActor ? html`
<form>
<nde-form-element .inverse="${true}" .actor="${this.formActor}" .translator="${this.translator}" field="webId">
<div slot="icon"></div>
<input type="text" slot="input" placeholder="${this.translator.translate('nde.features.authenticate.pages.login.search-placeholder')}" />
<input type="text" slot="input" placeholder="${this.translator?.translate('nde.features.authenticate.pages.login.search-placeholder')}" />
<button slot="action" class="primary" ?disabled="${!this.enableSubmit}" @click="${() => this.formActor.send(FormEvents.FORM_SUBMITTED)}">${ unsafeSVG(Login) }</button>
</nde-form-element>
</form>
` : html``}
</div>
<div></div>
` : html``;
`;
}

/**
Expand All @@ -107,23 +140,22 @@ export class AuthenticateRootComponent extends RxLitElement {
unsafeCSS(Theme),
css`
:host {
width: 400px;
height: 100%;
margin: auto;
display: flex;
flex-direction: column;
gap: 80px;
justify-content: center;
align-items: center;
gap: var(--gap-huge);
}
.title-container {
margin-top: 30vh;
max-height: 50px;
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
color: var(--colors-foreground-inverse);
width: 400px;
}
.title-container svg {
Expand All @@ -138,6 +170,15 @@ export class AuthenticateRootComponent extends RxLitElement {
font-size: var(--font-size-header-normal);
font-weight: normal;
}
.form-container {
width: 400px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
gap: var(--gap-large);
}
`,
];
}
Expand Down
Loading

0 comments on commit 7531abe

Please sign in to comment.