Skip to content

Commit 4fc7ccf

Browse files
committed
[APIM-11537] Fix PR comments.
1 parent 1125ae7 commit 4fc7ccf

File tree

21 files changed

+187
-203
lines changed

21 files changed

+187
-203
lines changed

gravitee-apim-portal-webui-next/projects/gravitee-markdown/src/lib/components/card/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Card Component
22

3-
A flexible card component for displaying structured content with optional title, subtitle, and markdown content.
3+
A flexible card component for displaying structured content with optional title, subtitle, and markdown content.
44
The component uses a token-based theming system that allows for easy customization through CSS custom properties and input properties.
55

66
## Features

gravitee-apim-portal-webui-next/src/app/app.config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ function initApp(
4343
): () => Observable<unknown> {
4444
return () =>
4545
configService.initBaseURL().pipe(
46-
switchMap(_ => authService.load()),
4746
switchMap(_ =>
4847
combineLatest([
4948
themeService.loadTheme(),
50-
currentUserService.loadUser(),
5149
configService.loadConfiguration(),
5250
portalMenuLinksService.loadCustomLinks(),
51+
authService.load().pipe(switchMap(_ => currentUserService.loadUser())),
5352
]),
5453
),
5554
catchError(error => {

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.html

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
limitations under the License.
1616
1717
-->
18-
<form [formGroup]="logInForm" (ngSubmit)="logIn()" class="log-in__form__container" [class.mobile]="isMobile()">
18+
<form [formGroup]="logInForm" (ngSubmit)="logIn()" class="log-in__form__container" [appIsMobile]="'log-in__form__container--mobile'">
1919
<mat-card appearance="outlined" class="log-in">
2020
<mat-card-header class="log-in__form__header">
2121
<mat-card-title class="log-in__form__title" i18n="@@logInTitle">Login</mat-card-title>
2222
</mat-card-header>
2323
<mat-card-content class="log-in__form__content">
24-
@if (isLocalLoginEnabled()) {
24+
@let identityProviders = identityProviders$ | async;
25+
@if (isLocalLoginEnabled) {
2526
<div class="log-in__form">
2627
<div class="log-in__form__fields">
2728
<mat-form-field appearance="outline" class="log-in__form__input">
@@ -39,40 +40,35 @@
3940
}
4041
</mat-form-field>
4142
@if (error() === 401) {
42-
<mat-error class="log-in__form__error" i18n="@@logInError401"
43+
<mat-error class="log-in__form__error m3-label-large" i18n="@@logInError401"
4344
>The username or password you entered is incorrect, please try again.</mat-error
4445
>
4546
}
4647
</div>
4748
<div class="log-in__form__buttons">
48-
<button
49-
[disabled]="logInForm.invalid"
50-
type="submit"
51-
mat-flat-button
52-
i18n="@@logInAction"
53-
class="log-in__form__submit primary-button">
49+
<button [disabled]="logInForm.invalid" type="submit" mat-flat-button i18n="@@logInAction" class="log-in__form__submit">
5450
Log in
5551
</button>
56-
<a class="log-in__form__forgot-password" i18n="@@resetPasswordAction" routerLink="reset-password"> Forgot password? </a>
52+
<a class="log-in__form__forgot-password m3-label-large" i18n="@@resetPasswordAction" routerLink="reset-password">
53+
Forgot password?
54+
</a>
5755
</div>
5856
</div>
59-
}
60-
<div class="log-in__sso">
61-
@if (isLocalLoginEnabled() && identityProviders.length > 0) {
62-
<div class="log-in__sso__separator">
63-
<div class="log-in__sso__separator__line"></div>
64-
<div class="log-in__sso__separator__label" i18n="@@orSeparator">OR</div>
57+
@if ((identityProviders ?? []).length > 0) {
58+
<div class="log-in__or-separator m3-label-large">
59+
<div class="log-in__or-separator__line"></div>
60+
<div class="log-in__or-separator__label" i18n="@@orSeparator">OR</div>
6561
</div>
6662
}
67-
@for (provider of identityProviders; track provider.id) {
68-
<button type="button" mat-stroked-button class="log-in__sso__idp secondary-button" (click)="authenticateSSO(provider)">
69-
<div class="log-in__sso__idp__container">
70-
<img class="log-in__sso__idp__logo" [src]="'assets/images/idp/' + getProviderLogo(provider)" alt="Provider Logo" />
71-
<span class="log-in__sso__idp__label" i18n="@@logInSsoLabelPrefix">Continue with {{ provider.name }}</span>
72-
</div>
73-
</button>
74-
}
75-
</div>
63+
}
64+
@for (provider of identityProviders; track provider.id) {
65+
<button type="button" mat-stroked-button class="log-in__sso-provider secondary-button" (click)="authenticateSSO(provider)">
66+
<div class="log-in__sso-provider__container">
67+
<img class="log-in__sso-provider__logo" [src]="'assets/images/idp/' + getProviderLogo(provider)" alt="Provider Logo" />
68+
<span class="log-in__sso-provider__label m3-label-large" i18n="@@logInSsoLabelPrefix">Continue with {{ provider.name }}</span>
69+
</div>
70+
</button>
71+
}
7672
</mat-card-content>
7773
</mat-card>
7874
</form>

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.scss

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
.log-in {
2424
display: flex;
2525
flex-flow: column;
26-
background: #{theme.$background-color};
2726
gap: 20px;
2827

2928
&__form {
@@ -35,16 +34,11 @@
3534
padding: 20px 20px 0;
3635
}
3736

38-
&__title {
39-
font-size: 18px;
40-
font-weight: 700;
41-
}
42-
4337
&__container {
4438
width: 400px;
4539
border-radius: 8px;
4640

47-
&.mobile {
41+
&--mobile {
4842
max-width: 75vw;
4943
}
5044
}
@@ -72,7 +66,6 @@
7266
&__forgot-password {
7367
margin-top: 5px;
7468
color: #{theme.$primary-main-color};
75-
font-size: 14px;
7669
text-align: center;
7770
text-decoration: none;
7871

@@ -82,58 +75,54 @@
8275
}
8376

8477
&__error {
85-
font-size: 14px;
8678
text-align: center;
8779
}
8880
}
8981

90-
&__sso {
82+
&__or-separator {
83+
position: relative;
9184
display: flex;
92-
flex-flow: column;
93-
gap: 16px;
85+
height: 30px;
86+
align-items: center;
87+
margin: 20px 0;
9488

95-
&__separator {
96-
position: relative;
89+
&__line {
90+
width: 100%;
91+
border-top: 1px solid #{theme.$card-border-color};
92+
}
93+
94+
&__label {
95+
position: absolute;
9796
display: flex;
98-
height: 30px;
97+
width: 40px;
98+
height: 100%;
9999
align-items: center;
100-
margin: 20px 0 4px;
101-
font-size: 14px;
100+
justify-content: center;
101+
margin: auto;
102+
background: #{theme.$card-background-color};
103+
inset: 0;
104+
}
105+
}
102106

103-
&__line {
104-
width: 100%;
105-
border-top: 1px solid #{theme.$card-border-color};
106-
}
107+
&__sso-provider {
108+
width: 100%;
109+
padding: 0 18px;
110+
margin-bottom: 16px;
111+
color: #{theme.$button-text-text-color};
107112

108-
&__label {
109-
position: absolute;
110-
display: flex;
111-
width: 40px;
112-
height: 100%;
113-
align-items: center;
114-
justify-content: center;
115-
margin: auto;
116-
background: #{theme.$background-color};
117-
inset: 0;
118-
}
113+
&:last-of-type {
114+
margin-bottom: 0;
119115
}
120116

121-
&__idp {
122-
width: 100%;
123-
padding: 0 18px;
124-
color: #{theme.$button-text-text-color};
125-
font-weight: bold;
126-
127-
&__container {
128-
display: flex;
129-
align-items: center;
130-
gap: 7px;
131-
}
117+
&__container {
118+
display: flex;
119+
align-items: center;
120+
gap: 7px;
121+
}
132122

133-
&__logo {
134-
width: 23px;
135-
height: 23px;
136-
}
123+
&__logo {
124+
width: 23px;
125+
height: 23px;
137126
}
138127
}
139128
}

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.spec.ts

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,47 @@ describe('LogInComponent', () => {
3434
let harnessLoader: HarnessLoader;
3535
let httpTestingController: HttpTestingController;
3636

37-
async function initComponent() {
38-
fixture = TestBed.createComponent(LogInComponent);
39-
harnessLoader = TestbedHarnessEnvironment.loader(fixture);
40-
httpTestingController = TestBed.inject(HttpTestingController);
41-
fixture.detectChanges();
42-
}
43-
44-
function enableLocalLogin(enable: boolean) {
45-
const configService = TestBed.inject(ConfigService) as unknown as ConfigServiceStub;
46-
configService.configuration.authentication!.localLogin!.enabled = enable;
47-
}
48-
49-
function initSsoProviders(providers: IdentityProvider[]) {
50-
const identityProviderService = TestBed.inject(IdentityProviderService) as unknown as IdentityProviderServiceStub;
51-
identityProviderService.providers = providers;
52-
}
53-
54-
beforeEach(async () => {
37+
const init = async (
38+
params: Partial<{ enableLocalLogin: boolean; ssoProviders: IdentityProvider[] }> = {
39+
enableLocalLogin: true,
40+
ssoProviders: [],
41+
},
42+
) => {
5543
await TestBed.configureTestingModule({
5644
imports: [LogInComponent, AppTestingModule],
45+
providers: [
46+
{
47+
provide: ConfigService,
48+
useFactory: () => {
49+
const stub = new ConfigServiceStub();
50+
stub.configuration.authentication!.localLogin!.enabled = params.enableLocalLogin;
51+
return stub;
52+
},
53+
},
54+
{
55+
provide: IdentityProviderService,
56+
useFactory: () => {
57+
const stub = new IdentityProviderServiceStub();
58+
stub.providers = params.ssoProviders!;
59+
return stub;
60+
},
61+
},
62+
],
5763
}).compileComponents();
5864

59-
await initComponent();
60-
});
65+
fixture = TestBed.createComponent(LogInComponent);
66+
harnessLoader = TestbedHarnessEnvironment.loader(fixture);
67+
httpTestingController = TestBed.inject(HttpTestingController);
68+
fixture.detectChanges();
69+
};
6170

6271
afterEach(() => {
6372
httpTestingController.verify();
6473
});
6574

6675
it('should allow submit if username and password are valid', async () => {
76+
await init();
77+
6778
const submitButton = await harnessLoader.getHarness(MatButtonHarness.with({ text: 'Log in' }));
6879

6980
const username = await harnessLoader.getHarness(MatInputHarness.with({ selector: '[formControlName="username"]' }));
@@ -78,6 +89,8 @@ describe('LogInComponent', () => {
7889
});
7990

8091
it('should not validate form with missing username', async () => {
92+
await init();
93+
8194
const submitButton = await harnessLoader.getHarness(MatButtonHarness.with({ text: 'Log in' }));
8295

8396
const password = await harnessLoader.getHarness(MatInputHarness.with({ selector: '[formControlName="password"]' }));
@@ -87,6 +100,8 @@ describe('LogInComponent', () => {
87100
});
88101

89102
it('should not validate form with missing password', async () => {
103+
await init();
104+
90105
const submitButton = await harnessLoader.getHarness(MatButtonHarness.with({ text: 'Log in' }));
91106

92107
const username = await harnessLoader.getHarness(MatInputHarness.with({ selector: '[formControlName="username"]' }));
@@ -96,6 +111,8 @@ describe('LogInComponent', () => {
96111
});
97112

98113
it('should login and fetch current user on submit', async () => {
114+
await init();
115+
99116
const submitButton = await harnessLoader.getHarness(MatButtonHarness.with({ text: 'Log in' }));
100117
const username = await harnessLoader.getHarness(MatInputHarness.with({ selector: '[formControlName="username"]' }));
101118
await username.setValue('[email protected]');
@@ -110,15 +127,15 @@ describe('LogInComponent', () => {
110127
});
111128

112129
it('should not display log-in form', async () => {
113-
enableLocalLogin(false);
114-
initSsoProviders([
115-
{
116-
id: 'github',
117-
name: 'GitHub',
118-
},
119-
]);
120-
121-
await initComponent();
130+
await init({
131+
enableLocalLogin: false,
132+
ssoProviders: [
133+
{
134+
id: 'github',
135+
name: 'GitHub',
136+
},
137+
],
138+
});
122139

123140
const login = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__form' }));
124141
expect(login).toBeNull();
@@ -134,10 +151,9 @@ describe('LogInComponent', () => {
134151
});
135152

136153
it('should not display SSO providers', async () => {
137-
enableLocalLogin(true);
138-
initSsoProviders([]);
139-
140-
await initComponent();
154+
await init({
155+
enableLocalLogin: true,
156+
});
141157

142158
const login = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__form' }));
143159
expect(login).not.toBeNull();
@@ -155,10 +171,10 @@ describe('LogInComponent', () => {
155171
{ id: 'google', name: 'Google' },
156172
{ id: 'graviteeio_am', name: 'Gravitee AM' },
157173
];
158-
enableLocalLogin(true);
159-
initSsoProviders(identityProviders);
160-
161-
await initComponent();
174+
await init({
175+
enableLocalLogin: true,
176+
ssoProviders: identityProviders,
177+
});
162178

163179
const orSeparator = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__sso__separator' }));
164180
expect(orSeparator).not.toBeNull();
@@ -174,13 +190,12 @@ describe('LogInComponent', () => {
174190
});
175191

176192
it('should redirect when clicked on SSO provider', async () => {
177-
enableLocalLogin(true);
178-
initSsoProviders([{ id: 'google', name: 'Google' }]);
179-
193+
await init({
194+
enableLocalLogin: true,
195+
ssoProviders: [{ id: 'google', name: 'Google' }],
196+
});
180197
const authenticateSSO = jest.spyOn(TestBed.inject(AuthService), 'authenticateSSO').mockReturnValue();
181198

182-
await initComponent();
183-
184199
const ssoProvider = await harnessLoader.getHarness(MatButtonHarness.with({ selector: '.log-in__sso__idp' }));
185200
await ssoProvider.click();
186201
expect(authenticateSSO).toHaveBeenCalledWith({ id: 'google', name: 'Google' }, '');

0 commit comments

Comments
 (0)