feat(settings): migrate SessionLifetimeSelect to React [BE-12583] (#1829)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,10 +15,15 @@ import { HiddenContainersPanel } from '@/react/portainer/settings/SettingsView/H
|
||||
import { SSLSettingsPanelWrapper } from '@/react/portainer/settings/SettingsView/SSLSettingsPanel/SSLSettingsPanel';
|
||||
import { AuthStyleField } from '@/react/portainer/settings/AuthenticationView/OAuth';
|
||||
import { AutoUserProvisionToggle } from '@/react/portainer/settings/AuthenticationView/AutoUserProvisionToggle/AutoUserProvisionToggle';
|
||||
import { SessionLifetimeSelect } from '@/react/portainer/settings/AuthenticationView/SessionLifetimeSelect';
|
||||
|
||||
export const settingsModule = angular
|
||||
.module('portainer.app.react.components.settings', [])
|
||||
.component('settingsOpenAmt', r2a(SettingsOpenAMT, ['onSubmit', 'settings']))
|
||||
.component(
|
||||
'sessionLifetimeSelect',
|
||||
r2a(SessionLifetimeSelect, ['value', 'onChange'])
|
||||
)
|
||||
.component(
|
||||
'internalAuth',
|
||||
r2a(InternalAuth, ['onSaveSettings', 'isLoading', 'value', 'onChange'])
|
||||
|
||||
@@ -144,7 +144,7 @@ BE-6604 (Parent: Authentication Settings Migration)
|
||||
|
||||
### Session & Page Structure (Parallel - No Dependencies)
|
||||
|
||||
- [ ] PR 1: [BE-12583](https://linear.app/portainer/issue/BE-12583) SessionLifetimeSelect → react2angular bridge
|
||||
- [x] PR 1: [BE-12583](https://linear.app/portainer/issue/BE-12583) SessionLifetimeSelect → react2angular bridge
|
||||
- [x] PR 2: [BE-12584](https://linear.app/portainer/issue/BE-12584) AuthenticationMethodSelector wrapper (BoxSelector is already React)
|
||||
|
||||
### Internal Auth (Already Done!)
|
||||
|
||||
@@ -7,21 +7,12 @@
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="authSettingsForm">
|
||||
<div class="col-sm-12 form-section-title"> Configuration </div>
|
||||
<div class="form-group">
|
||||
<label for="user_timeout" class="col-sm-2 control-label text-left">
|
||||
Session lifetime
|
||||
<portainer-tooltip message="'Time before users are forced to relogin.'"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select
|
||||
id="user_timeout"
|
||||
data-cy="user-timeout-select"
|
||||
class="form-control"
|
||||
ng-model="settings.UserSessionTimeout"
|
||||
ng-options="opt.value as opt.key for opt in state.availableUserSessionTimeoutOptions"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<session-lifetime-select
|
||||
value="settings.UserSessionTimeout"
|
||||
on-change="(onChangeSessionLifetime)">
|
||||
</session-lifetime-select>
|
||||
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small vertical-center">
|
||||
<pr-icon icon="'alert-triangle'" class-name="'icon-sm icon-yellow'"></pr-icon>
|
||||
|
||||
@@ -4,6 +4,7 @@ import _ from 'lodash-es';
|
||||
import { buildLdapSettingsModel, buildAdSettingsModel } from '@/portainer/settings/authentication/ldap/ldap-settings.model';
|
||||
import { SERVER_TYPES } from '@/react/portainer/settings/AuthenticationView/ldap-options';
|
||||
import { AuthenticationMethod } from '@/react/portainer/settings/types';
|
||||
import { getDefaultValue as getDefaultSessionValue } from '@/react/portainer/settings/AuthenticationView/SessionLifetimeSelect';
|
||||
|
||||
angular.module('portainer.app').controller('SettingsAuthenticationController', SettingsAuthenticationController);
|
||||
|
||||
@@ -13,36 +14,10 @@ function SettingsAuthenticationController($q, $scope, $state, Notifications, Set
|
||||
$scope.state = {
|
||||
uploadInProgress: false,
|
||||
actionInProgress: false,
|
||||
availableUserSessionTimeoutOptions: [
|
||||
{
|
||||
key: '30 minutes',
|
||||
value: '30m',
|
||||
},
|
||||
{
|
||||
key: '1 hour',
|
||||
value: '1h',
|
||||
},
|
||||
{
|
||||
key: '4 hours',
|
||||
value: '4h',
|
||||
},
|
||||
{
|
||||
key: '8 hours',
|
||||
value: '8h',
|
||||
},
|
||||
{
|
||||
key: '24 hours',
|
||||
value: '24h',
|
||||
},
|
||||
{ key: '1 week', value: `${24 * 7}h` },
|
||||
{ key: '1 month', value: `${24 * 30}h` },
|
||||
{ key: '6 months', value: `${24 * 30 * 6}h` },
|
||||
{ key: '1 year', value: `${24 * 30 * 12}h` },
|
||||
],
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
UserSessionTimeout: $scope.state.availableUserSessionTimeoutOptions[0],
|
||||
UserSessionTimeout: getDefaultSessionValue(),
|
||||
TLSCACert: '',
|
||||
ldap: {
|
||||
serverType: 0,
|
||||
@@ -76,6 +51,12 @@ function SettingsAuthenticationController($q, $scope, $state, Notifications, Set
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onChangeSessionLifetime = function onChangeSessionLifetime(value) {
|
||||
$scope.$evalAsync(() => {
|
||||
$scope.settings.UserSessionTimeout = value;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.authenticationMethodSelected = function authenticationMethodSelected(value) {
|
||||
if (!$scope.settings) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { SessionLifetimeSelect } from './SessionLifetimeSelect';
|
||||
|
||||
describe('SessionLifetimeSelect', () => {
|
||||
test('should render with the correct label and tooltip', () => {
|
||||
const onChange = vi.fn();
|
||||
render(<SessionLifetimeSelect value="1h" onChange={onChange} />);
|
||||
|
||||
expect(screen.getByLabelText('Session lifetime')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should call onChange when selecting a new value', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = vi.fn();
|
||||
render(<SessionLifetimeSelect value="1h" onChange={onChange} />);
|
||||
|
||||
const select = screen.getByRole('combobox');
|
||||
await user.selectOptions(select, '8h');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith('8h');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { FormControl } from '@@/form-components/FormControl';
|
||||
import { Select } from '@@/form-components/Input';
|
||||
|
||||
const sessionLifetimeOptions = [
|
||||
{
|
||||
key: '30 minutes',
|
||||
value: '30m',
|
||||
},
|
||||
{
|
||||
key: '1 hour',
|
||||
value: '1h',
|
||||
},
|
||||
{
|
||||
key: '4 hours',
|
||||
value: '4h',
|
||||
},
|
||||
{
|
||||
key: '8 hours',
|
||||
value: '8h',
|
||||
},
|
||||
{
|
||||
key: '24 hours',
|
||||
value: '24h',
|
||||
},
|
||||
{ key: '1 week', value: `${24 * 7}h` },
|
||||
{ key: '1 month', value: `${24 * 30}h` },
|
||||
{ key: '6 months', value: `${24 * 30 * 6}h` },
|
||||
{ key: '1 year', value: `${24 * 30 * 12}h` },
|
||||
] as const;
|
||||
|
||||
export function getDefaultValue() {
|
||||
return sessionLifetimeOptions[0];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
onChange(value: string): void;
|
||||
}
|
||||
|
||||
export function SessionLifetimeSelect({ value, onChange }: Props) {
|
||||
return (
|
||||
<FormControl
|
||||
inputId="user_timeout"
|
||||
label="Session lifetime"
|
||||
tooltip="Time before users are forced to relogin."
|
||||
>
|
||||
<Select
|
||||
id="user_timeout"
|
||||
data-cy="user-timeout-select"
|
||||
name="user_timeout"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
options={sessionLifetimeOptions.map((opt) => ({
|
||||
label: opt.key,
|
||||
value: opt.value,
|
||||
}))}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user