Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

[Backport/need review] fix(UI): Fix layout for Safari and form validation #11440

Merged
merged 18 commits into from
Jul 29, 2022
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
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ try {
checkout scm
}
sh 'rm -rf *.deb'
sh 'docker run -i --entrypoint /src/centreon/ci/scripts/centreon-deb-package.sh -w "/src" -v "$PWD:/src" -e DISTRIB="bullseye" -e VERSION=$VERSION -e RELEASE=$RELEASE registry.centreon.com/centreon-debian11-dependencies:22.04'
sh 'docker run -i --entrypoint /src/centreon/ci/scripts/centreon-deb-package.sh -w "/src" -v "$PWD:/src" -e DISTRIB="bullseye" -e VERSION=$VERSION -e RELEASE=$RELEASE registry.centreon.com/mon-build-dependencies-22.04:debian11'
stash name: 'Debian11', includes: '*.deb'
archiveArtifacts artifacts: "*"
sh 'rm -rf *.deb'
Expand Down
2 changes: 1 addition & 1 deletion ci/debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ override_dh_clean:

override_dh_auto_build:
composer install --no-dev --optimize-autoloader -n
npm ci
npm ci --legacy-peer-deps
npm run build
find . -type f | \
grep -v debian/extra/centreon-web/centreon-macroreplacement.txt | \
Expand Down
6 changes: 3 additions & 3 deletions ci/scripts/centreon-deb-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ for i in lang/* ; do
done
rm -rf lang

# Install npm dependency
apt-get update
apt install -y npm
# Generate API documentation.
npm i -g redoc-cli
redoc-cli build --options.hideDownloadButton=true doc/API/centreon-api-v${MAJOR_VERSION}.yaml -o ../centreon-api-v${MAJOR_VERSION}.html

# Make tar with original content
cd ..
Expand Down
18 changes: 2 additions & 16 deletions www/front_src/src/Authentication/Openid/Form/inputs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { equals, isEmpty, isNil, not, path, prop } from 'ramda';
import { equals, isEmpty, not, prop } from 'ramda';
import { FormikValues } from 'formik';

import {
Expand Down Expand Up @@ -32,7 +32,7 @@ import {
labelDeleteRelation,
labelAuthorizationKey,
} from '../translatedLabels';
import { AuthenticationType, AuthorizationRule } from '../models';
import { AuthenticationType } from '../models';
import { InputProps, InputType } from '../../FormInputs/models';
import {
labelActivation,
Expand Down Expand Up @@ -249,20 +249,6 @@ export const inputs: Array<InputProps> = [
claimValue: '',
},
deleteLabel: labelDeleteRelation,
getRequired: ({ values, index }): boolean => {
const tableValues = prop('authorizationRules', values);

const rowValues = path<AuthorizationRule>(
['authorizationRules', index],
values,
);

return isNil(prop('contactGroup', values))
? not(isNil(rowValues))
: isNil(tableValues) ||
isEmpty(rowValues?.claimValue) ||
isNil(rowValues?.accessGroup);
},
},
label: labelDefineRelationAuthorizationValueAndAccessGroup,
type: InputType.FieldsTable,
Expand Down
41 changes: 2 additions & 39 deletions www/front_src/src/Authentication/Openid/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -445,11 +445,10 @@ describe('Openid configuration form', () => {
accessGroupsEndpoint,
labelAccessGroup,
'Access Group 2',
1,
],
])(
'updates the %p field when an option is selected from the retrieved options',
async (_, retrievedOptions, endpoint, label, value, index = 0) => {
async (_, retrievedOptions, endpoint, label, value) => {
mockGetRequestsWithNoAuthorizationConfiguration();
renderOpenidConfigurationForm();

Expand Down Expand Up @@ -484,7 +483,7 @@ describe('Openid configuration form', () => {
userEvent.click(screen.getByText(value));

await waitFor(() => {
expect(screen.getAllByLabelText(label)[index]).toHaveValue(value);
expect(screen.getAllByLabelText(label)[0]).toHaveValue(value);
});
},
);
Expand All @@ -508,40 +507,4 @@ describe('Openid configuration form', () => {
);
});
});

it('displays the "Authorization value" and "Access group" fields as required when the "Contact group" field is filled', async () => {
mockGetRequestsWithNoAuthorizationConfiguration();
mockedAxios.get.mockResolvedValueOnce({
data: retrievedContactGroups,
});

renderOpenidConfigurationForm();

await waitFor(() => {
expect(screen.getByLabelText(labelContactGroup)).toBeInTheDocument();
});

userEvent.click(screen.getByLabelText(labelContactGroup));

await waitFor(() => {
expect(mockedAxios.get).toHaveBeenCalledWith(
`${contactGroupsEndpoint}?page=1&sort_by=${encodeURIComponent(
'{"name":"ASC"}',
)}`,
cancelTokenRequestParam,
);
});

await waitFor(() => {
expect(screen.getByText('Contact Group 1')).toBeInTheDocument();
});

userEvent.click(screen.getByText('Contact Group 1'));

await waitFor(() => {
expect(screen.getByLabelText(labelAuthorizationValue)).toHaveAttribute(
'required',
);
});
});
});
11 changes: 1 addition & 10 deletions www/front_src/src/Authentication/Openid/useValidationSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
labelRequired,
labelInvalidURL,
labelInvalidIPAddress,
labelAtLeastOneAuthorizationIsRequired,
} from './translatedLabels';

const IPAddressRegexp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,3})?$/;
Expand All @@ -29,15 +28,7 @@ const useValidationSchema = (): Yup.SchemaOf<OpenidConfiguration> => {
return Yup.object({
authenticationType: Yup.string().required(t(labelRequired)),
authorizationEndpoint: Yup.string().nullable().required(t(labelRequired)),
authorizationRules: Yup.array()
.of(authorizationSchema)
.when('contactGroup', (contactGroup, schema) => {
return contactGroup
? schema
.min(1, t(labelAtLeastOneAuthorizationIsRequired))
.required(t(labelRequired))
: schema.nullable();
}),
authorizationRules: Yup.array().of(authorizationSchema),
autoImport: Yup.boolean().required(t(labelRequired)),
baseUrl: Yup.string()
.matches(urlRegexp, t(labelInvalidURL))
Expand Down
74 changes: 46 additions & 28 deletions www/front_src/src/Authentication/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { useMemo } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';

import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
import { useUpdateAtom } from 'jotai/utils';
import { Responsive } from '@visx/visx';

import { Box, Container, Paper, Tab } from '@mui/material';
import { TabContext, TabList, TabPanel } from '@mui/lab';
Expand Down Expand Up @@ -90,35 +89,39 @@ const useStyles = makeStyles((theme) => ({
formContainer: {
display: 'grid',
gridTemplateColumns: '1.2fr 0.6fr',
height: '100%',
overflowY: 'auto',
justifyItems: 'center',
padding: theme.spacing(3),
},
image: {
height: '200px',
opacity: 0.5,
padding: theme.spacing(0, 5),
position: 'sticky',
top: 0,
width: '200px',
},
panel: {
height: '80%',
padding: 0,
},
paper: {
boxShadow: theme.shadows[3],
height: '100%',
},
tabList: {
boxShadow: theme.shadows[2],
},
}));

const marginBottomHeight = 88;
const scrollMargin = 8;

const Authentication = (): JSX.Element => {
const classes = useStyles();
const { t } = useTranslation();

const formContainerRef = useRef<HTMLDivElement | null>(null);

const [windowHeight, setWindowHeight] = useState(window.innerHeight);
const [clientRect, setClientRect] = useState<DOMRect | null>(null);

const appliedTab = useAtomValue(appliedTabAtom);
const { themeMode } = useAtomValue(userAtom);
const setTab = useUpdateAtom(tabAtom);
Expand All @@ -127,6 +130,23 @@ const Authentication = (): JSX.Element => {
setTab(newTab);
};

const resize = (): void => {
setWindowHeight(window.innerHeight);
};

useEffect(() => {
window.addEventListener('resize', resize);

setClientRect(formContainerRef.current?.getBoundingClientRect() ?? null);

return () => {
window.removeEventListener('resize', resize);
};
}, []);

const formContainerHeight =
windowHeight - (clientRect?.top || 0) - scrollMargin;

const tabs = useMemo(
() =>
panels.map(({ title, value }) => (
Expand All @@ -136,33 +156,31 @@ const Authentication = (): JSX.Element => {
);

const tabPanels = useMemo(
() => (
<Responsive.ParentSize>
{({ height }): Array<JSX.Element> =>
panels.map(({ Component, value, image }) => (
<TabPanel
className={classes.panel}
key={value}
style={{ height: height - marginBottomHeight }}
value={value}
>
<div className={classes.formContainer}>
<Component />
<img alt="padlock" className={classes.image} src={image} />
</div>
</TabPanel>
))
}
</Responsive.ParentSize>
),
[themeMode],
() =>
panels.map(({ Component, value, image }) => (
<TabPanel className={classes.panel} key={value} value={value}>
<Box
ref={formContainerRef}
sx={{
height: `${formContainerHeight}px`,
overflowY: 'auto',
}}
>
<div className={classes.formContainer}>
<Component />
<img alt="padlock" className={classes.image} src={image} />
</div>
</Box>
</TabPanel>
)),
[themeMode, formContainerHeight],
);

return (
<Box className={classes.box}>
<TabContext value={appliedTab}>
<Container className={classes.container}>
<Paper className={classes.paper}>
<Paper square className={classes.paper}>
<TabList
className={classes.tabList}
variant="fullWidth"
Expand Down