value: parentValue,
searchInputAriaLabel,
} = props;
-
const intl = useIntl();
const input = useRef<null | HTMLElement>(null);
const [value, setValue] = useState(parentValue ?? '');
() =>
debounce((val: string) => {
onChange(val);
+ setDirty(false);
}, DEBOUNCE_DELAY),
[onChange],
);
'/admin/settings/encryption',
'/admin/extension/license/support',
'/admin/audit',
+ '/admin/projects_management',
];
export default function GlobalContainer() {
import 'core-js/stable';
/* */
import axios from 'axios';
+import 'react-day-picker/dist/style.css';
import { getAvailableFeatures } from '../api/features';
import { getGlobalNavigation } from '../api/navigation';
import { getCurrentUser } from '../api/users';
} from 'design-system';
import * as React from 'react';
import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions';
-import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
import { PermissionTemplate } from '../../../../types/types';
)}
{!this.state.done && !this.state.loading && (
- <>
- <MandatoryFieldsExplanation className="sw-mb-4" />
- <FormField
- label={translate('template')}
- required
- htmlFor="project-permissions-template-input"
- >
- {this.state.permissionTemplates && (
- <InputSelect
- size="full"
- id="project-permissions-template"
- inputId="project-permissions-template-input"
- onChange={this.handlePermissionTemplateChange}
- options={options}
- value={options.filter((o) => o.value === this.state.permissionTemplate)}
- />
- )}
- </FormField>
- </>
+ <FormField
+ label={translate('template')}
+ required
+ htmlFor="project-permissions-template-input"
+ >
+ {this.state.permissionTemplates && (
+ <InputSelect
+ size="full"
+ id="project-permissions-template"
+ inputId="project-permissions-template-input"
+ onChange={this.handlePermissionTemplateChange}
+ options={options}
+ value={options.filter((o) => o.value === this.state.permissionTemplate)}
+ />
+ )}
+ </FormField>
)}
</div>
</form>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ ButtonPrimary,
+ FlagMessage,
+ FormField,
+ InputSelect,
+ LabelValueSelectOption,
+ Modal,
+ Spinner,
+} from 'design-system';
import * as React from 'react';
import { bulkApplyTemplate, getPermissionTemplates } from '../../api/permissions';
import { Project } from '../../api/project-management';
-import Modal from '../../components/controls/Modal';
-import Select from '../../components/controls/Select';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
-import { Alert } from '../../components/ui/Alert';
-import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker';
import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
import { toISO8601WithOffsetString } from '../../helpers/dates';
import { addGlobalErrorMessageFromAPI } from '../../helpers/globalMessages';
submitting: boolean;
}
+const FORM_ID = 'bulk-apply-template-form';
+
export default class BulkApplyTemplateModal extends React.PureComponent<Props, State> {
mounted = false;
state: State = { done: false, loading: true, submitting: false };
);
}
- handleConfirmClick = () => {
+ handleConfirmClick = (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
const { analyzedBefore } = this.props;
const { permissionTemplate } = this.state;
if (permissionTemplate) {
}
};
- handlePermissionTemplateChange = ({ value }: { value: string }) => {
+ handlePermissionTemplateChange = ({ value }: LabelValueSelectOption<string>) => {
this.setState({ permissionTemplate: value });
};
if (isSelectionOnlyManaged) {
return (
- <Alert variant="error">
+ <FlagMessage variant="error" className="sw-my-2">
{translate(
'permission_templates.bulk_apply_permission_template.apply_to_only_github_projects',
)}
- </Alert>
+ </FlagMessage>
);
} else if (isSelectionOnlyLocal) {
return (
- <Alert variant="warning">
+ <FlagMessage variant="warning" className="sw-my-2">
{this.props.selection.length
? translateWithParameters(
'permission_templates.bulk_apply_permission_template.apply_to_selected',
'permission_templates.bulk_apply_permission_template.apply_to_all',
this.props.total,
)}
- </Alert>
+ </FlagMessage>
);
}
return (
- <Alert variant="warning">
+ <FlagMessage variant="warning" className="sw-my-2">
{translateWithParameters(
'permission_templates.bulk_apply_permission_template.apply_to_selected',
localProjects.length,
'permission_templates.bulk_apply_permission_template.apply_to_github_projects',
managedProjects.length,
)}
- </Alert>
+ </FlagMessage>
);
};
? this.state.permissionTemplates.map((t) => ({ label: t.name, value: t.id }))
: [];
return (
- <div className="modal-field">
- <label htmlFor="bulk-apply-template-input">
- {translate('template')}
- <MandatoryFieldMarker />
- </label>
- <Select
+ <FormField htmlFor="bulk-apply-template-input" label={translate('template')} required>
+ <InputSelect
id="bulk-apply-template"
inputId="bulk-apply-template-input"
isDisabled={this.state.submitting || isSelectionOnlyManaged}
onChange={this.handlePermissionTemplateChange}
options={options}
value={options.find((option) => option.value === this.state.permissionTemplate)}
+ size="auto"
/>
- </div>
+ </FormField>
);
};
const header = translate('permission_templates.bulk_apply_permission_template');
const isSelectionOnlyManaged = this.props.selection.every((s) => s.managed === true);
+ const body = (
+ <form id={FORM_ID} onSubmit={this.handleConfirmClick}>
+ {done && (
+ <FlagMessage variant="success">
+ {translate('projects_role.apply_template.success')}
+ </FlagMessage>
+ )}
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <div className="modal-body">
- {done && (
- <Alert variant="success">{translate('projects_role.apply_template.success')}</Alert>
- )}
-
- {loading && <i className="spinner" />}
-
- {!loading && !done && permissionTemplates && (
- <>
- <MandatoryFieldsExplanation className="spacer-bottom" />
- {this.renderWarning()}
- {this.renderSelect(isSelectionOnlyManaged)}
- </>
- )}
- </div>
+ <Spinner loading={loading} />
- <footer className="modal-foot">
- {submitting && <i className="spinner spacer-right" />}
- {!loading && !done && permissionTemplates && (
- <SubmitButton
+ {!loading && !done && permissionTemplates && (
+ <>
+ <MandatoryFieldsExplanation className="sw-mb-2" />
+ {this.renderWarning()}
+ {this.renderSelect(isSelectionOnlyManaged)}
+ </>
+ )}
+ </form>
+ );
+ return (
+ <Modal
+ isScrollable={false}
+ isOverflowVisible
+ headerTitle={header}
+ onClose={this.props.onClose}
+ loading={submitting}
+ body={body}
+ primaryButton={
+ !loading &&
+ !done &&
+ permissionTemplates && (
+ <ButtonPrimary
+ autoFocus
disabled={submitting || isSelectionOnlyManaged}
- onClick={this.handleConfirmClick}
+ form={FORM_ID}
+ type="submit"
>
{translate('apply')}
- </SubmitButton>
- )}
- <ResetButtonLink onClick={this.props.onClose}>
- {done ? translate('close') : translate('cancel')}
- </ResetButtonLink>
- </footer>
- </Modal>
+ </ButtonPrimary>
+ )
+ }
+ secondaryButtonLabel={done ? translate('close') : translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, FlagMessage, Modal, RadioButton, TextSubdued } from 'design-system';
import React, { useState } from 'react';
-import Modal from '../../components/controls/Modal';
-import Radio from '../../components/controls/Radio';
-import { Button, ResetButtonLink } from '../../components/controls/buttons';
-import { Alert } from '../../components/ui/Alert';
import { translate } from '../../helpers/l10n';
import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github';
import { Visibility } from '../../types/component';
onConfirm: (visiblity: Visibility) => void;
}
+const FORM_ID = 'change-default-visibility-form';
+
export default function ChangeDefaultVisibilityForm(props: Props) {
const [visibility, setVisibility] = useState(props.defaultVisibility);
const { data: githubProbivisioningEnabled } = useGithubProvisioningEnabledQuery();
- const handleConfirmClick = () => {
+ const handleConfirmClick = (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
props.onConfirm(visibility);
props.onClose();
};
const header = translate('settings.projects.change_visibility_form.header');
- return (
- <Modal contentLabel={header} onRequestClose={props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <div className="modal-body">
- {Object.values(Visibility).map((visibilityValue) => (
- <div className="big-spacer-bottom" key={visibilityValue}>
- <Radio
- value={visibilityValue}
- checked={visibility === visibilityValue}
- onCheck={handleVisibilityChange}
- >
- <div>
- {translate('visibility', visibilityValue)}
- <p className="text-muted spacer-top">
- {translate('visibility', visibilityValue, 'description.short')}
- </p>
- </div>
- </Radio>
- </div>
- ))}
-
- <Alert variant="warning">
- {translate(
- `settings.projects.change_visibility_form.warning${
- githubProbivisioningEnabled ? '.github' : ''
- }`,
- )}
- </Alert>
- </div>
+ const body = (
+ <form id={FORM_ID} onSubmit={handleConfirmClick}>
+ {Object.values(Visibility).map((visibilityValue) => (
+ <div className="sw-mb-4" key={visibilityValue}>
+ <RadioButton
+ value={visibilityValue}
+ checked={visibility === visibilityValue}
+ onCheck={handleVisibilityChange}
+ >
+ <div>
+ {translate('visibility', visibilityValue)}
+ <TextSubdued as="p" className="sw-mt-2">
+ {translate('visibility', visibilityValue, 'description.short')}
+ </TextSubdued>
+ </div>
+ </RadioButton>
+ </div>
+ ))}
+ <FlagMessage variant="warning">
+ {translate(
+ `settings.projects.change_visibility_form.warning${
+ githubProbivisioningEnabled ? '.github' : ''
+ }`,
+ )}
+ </FlagMessage>
+ </form>
+ );
- <footer className="modal-foot">
- <Button className="js-confirm" type="submit" onClick={handleConfirmClick}>
+ return (
+ <Modal
+ isScrollable={false}
+ isOverflowVisible
+ headerTitle={header}
+ onClose={props.onClose}
+ body={body}
+ primaryButton={
+ <ButtonPrimary form={FORM_ID} autoFocus type="submit">
{translate('settings.projects.change_visibility_form.submit')}
- </Button>
- <ResetButtonLink className="js-modal-close" onClick={props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </footer>
- </Modal>
+ </ButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { DangerButtonPrimary, FlagMessage, Modal } from 'design-system';
import * as React from 'react';
import { Project, bulkDeleteProjects } from '../../api/project-management';
-import Modal from '../../components/controls/Modal';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
-import { Alert } from '../../components/ui/Alert';
import { toISO8601WithOffsetString } from '../../helpers/dates';
import { translate, translateWithParameters } from '../../helpers/l10n';
};
renderWarning = () => (
- <Alert variant="warning">
+ <FlagMessage variant="warning">
{this.props.selection.length
? translateWithParameters(
'projects_management.delete_selected_warning',
this.props.selection.length,
)
: translateWithParameters('projects_management.delete_all_warning', this.props.total)}
- </Alert>
+ </FlagMessage>
);
render() {
const header = translate('qualifiers.delete', this.props.qualifier);
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <div className="modal-body">
- {this.renderWarning()}
- {translate('qualifiers.delete_confirm', this.props.qualifier)}
- </div>
-
- <footer className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <SubmitButton
- className="button-red"
+ <Modal
+ headerTitle={header}
+ onClose={this.props.onClose}
+ body={
+ <>
+ {this.renderWarning()}
+ <p className="sw-mt-2">
+ {translate('qualifiers.delete_confirm', this.props.qualifier)}
+ </p>
+ </>
+ }
+ primaryButton={
+ <DangerButtonPrimary
+ autoFocus
disabled={this.state.loading}
onClick={this.handleConfirmClick}
+ type="submit"
>
{translate('delete')}
- </SubmitButton>
- <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
- </footer>
- </Modal>
+ </DangerButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, InteractiveIcon, PencilIcon, Title } from 'design-system';
import * as React from 'react';
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
-import { Button, EditButton } from '../../components/controls/buttons';
import { translate } from '../../helpers/l10n';
import { Visibility } from '../../types/component';
import ChangeDefaultVisibilityForm from './ChangeDefaultVisibilityForm';
const { defaultProjectVisibility, hasProvisionPermission } = props;
return (
- <header className="page-header">
- <h1 className="page-title">{translate('projects_management')}</h1>
+ <header className="sw-mb-5">
+ <div className="sw-flex sw-items-center sw-justify-between">
+ <Title className="sw-m-0">{translate('projects_management')}</Title>
+ <div className="sw-flex sw-items-center it__page-actions">
+ <div className="sw-mr-2">
+ <span className="sw-mr-1">
+ {translate('settings.projects.default_visibility_of_new_projects')}{' '}
+ <strong className="sw-body-sm-highlight">
+ {defaultProjectVisibility ? translate('visibility', defaultProjectVisibility) : '—'}
+ </strong>
+ </span>
+ <InteractiveIcon
+ className="it__change-visibility"
+ Icon={PencilIcon}
+ onClick={() => setVisibilityForm(true)}
+ aria-label={translate('settings.projects.change_visibility_form.label')}
+ />
+ </div>
- <div className="page-actions">
- <span className="big-spacer-right">
- <span className="text-middle">
- {translate('settings.projects.default_visibility_of_new_projects')}{' '}
- <strong>
- {defaultProjectVisibility ? translate('visibility', defaultProjectVisibility) : '—'}
- </strong>
- </span>
- <EditButton
- className="js-change-visibility spacer-left button-small"
- onClick={() => setVisibilityForm(true)}
- aria-label={translate('settings.projects.change_visibility_form.label')}
- />
- </span>
-
- {hasProvisionPermission && (
- <Button
- id="create-project"
- onClick={() =>
- navigate('/projects/create?mode=manual', {
- state: { from: location.pathname },
- })
- }
- >
- {translate('qualifiers.create.TRK')}
- </Button>
- )}
+ {hasProvisionPermission && (
+ <ButtonPrimary
+ id="create-project"
+ onClick={() =>
+ navigate('/projects/create?mode=manual', {
+ state: { from: location.pathname },
+ })
+ }
+ >
+ {translate('qualifiers.create.TRK')}
+ </ButtonPrimary>
+ )}
+ </div>
</div>
- <p className="page-description">{translate('projects_management.page.description')}</p>
+ <p className="sw-mt-4">{translate('projects_management.page.description')}</p>
{visibilityForm && (
<ChangeDefaultVisibilityForm
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { LargeCenteredLayout, PageContentFontWrapper } from 'design-system';
import { debounce, uniq } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
const { currentUser } = this.props;
const { defaultProjectVisibility } = this.state;
return (
- <main className="page page-limited" id="projects-management-page">
- <Suggestions suggestions="projects_management" />
- <Helmet defer={false} title={translate('projects_management')} />
-
- <Header
- defaultProjectVisibility={defaultProjectVisibility}
- hasProvisionPermission={hasGlobalPermission(currentUser, Permissions.ProjectCreation)}
- onChangeDefaultProjectVisibility={this.handleDefaultProjectVisibilityChange}
- />
-
- <Search
- analyzedBefore={this.state.analyzedBefore}
- onAllDeselected={this.onAllDeselected}
- onAllSelected={this.onAllSelected}
- onDateChanged={this.handleDateChanged}
- onDeleteProjects={this.requestProjects}
- onProvisionedChanged={this.onProvisionedChanged}
- onQualifierChanged={this.onQualifierChanged}
- onSearch={this.onSearch}
- onVisibilityChanged={this.onVisibilityChanged}
- projects={this.state.projects}
- provisioned={this.state.provisioned}
- qualifiers={this.state.qualifiers}
- query={this.state.query}
- ready={this.state.ready}
- selection={this.state.selection}
- total={this.state.total}
- visibility={this.state.visibility}
- />
-
- <Projects
- currentUser={this.props.currentUser}
- onProjectDeselected={this.onProjectDeselected}
- onProjectSelected={this.onProjectSelected}
- projects={this.state.projects}
- ready={this.state.ready}
- selection={this.state.selection}
- />
-
- <ListFooter
- count={this.state.projects.length}
- loadMore={this.loadMore}
- ready={this.state.ready}
- total={this.state.total}
- />
- </main>
+ <LargeCenteredLayout as="main" id="projects-management-page">
+ <PageContentFontWrapper className="sw-body-sm sw-my-8">
+ <Suggestions suggestions="projects_management" />
+ <Helmet defer={false} title={translate('projects_management')} />
+
+ <Header
+ defaultProjectVisibility={defaultProjectVisibility}
+ hasProvisionPermission={hasGlobalPermission(currentUser, Permissions.ProjectCreation)}
+ onChangeDefaultProjectVisibility={this.handleDefaultProjectVisibilityChange}
+ />
+
+ <Search
+ analyzedBefore={this.state.analyzedBefore}
+ onAllDeselected={this.onAllDeselected}
+ onAllSelected={this.onAllSelected}
+ onDateChanged={this.handleDateChanged}
+ onDeleteProjects={this.requestProjects}
+ onProvisionedChanged={this.onProvisionedChanged}
+ onQualifierChanged={this.onQualifierChanged}
+ onSearch={this.onSearch}
+ onVisibilityChanged={this.onVisibilityChanged}
+ projects={this.state.projects}
+ provisioned={this.state.provisioned}
+ qualifiers={this.state.qualifiers}
+ query={this.state.query}
+ ready={this.state.ready}
+ selection={this.state.selection}
+ total={this.state.total}
+ visibility={this.state.visibility}
+ />
+
+ <Projects
+ currentUser={this.props.currentUser}
+ onProjectDeselected={this.onProjectDeselected}
+ onProjectSelected={this.onProjectSelected}
+ projects={this.state.projects}
+ ready={this.state.ready}
+ selection={this.state.selection}
+ />
+
+ <ListFooter
+ count={this.state.projects.length}
+ loadMore={this.loadMore}
+ ready={this.state.ready}
+ total={this.state.total}
+ useMIUIButtons
+ />
+ </PageContentFontWrapper>
+ </LargeCenteredLayout>
);
}
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-.project-row-text-cell {
- max-width: 20em;
-}
-
-.projects-management-search {
- display: flex;
- gap: 20px;
- padding: 8px 10px;
- flex-wrap: wrap;
-}
-
-.projects-management-search > * {
- display: flex;
- white-space: nowrap;
-}
-
-.projects-management-search > *:not(.bulk-actions) {
- flex-direction: column;
- justify-content: center;
-}
-
-.projects-management-search > .bulk-actions {
- justify-content: end;
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ActionCell, Badge, Checkbox, ContentCell, HoverLink, Note, TableRow } from 'design-system';
import * as React from 'react';
import { Project } from '../../api/project-management';
-import Link from '../../components/common/Link';
import PrivacyBadgeContainer from '../../components/common/PrivacyBadgeContainer';
-import Checkbox from '../../components/controls/Checkbox';
import Tooltip from '../../components/controls/Tooltip';
-import QualifierIcon from '../../components/icons/QualifierIcon';
import DateFormatter from '../../components/intl/DateFormatter';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { getComponentOverviewUrl } from '../../helpers/urls';
import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github';
import { ComponentQualifier } from '../../types/component';
import { LoggedInUser } from '../../types/users';
-import './ProjectRow.css';
import ProjectRowActions from './ProjectRowActions';
interface Props {
};
return (
- <tr data-project-key={project.key}>
- <td className="thin">
+ <TableRow data-project-key={project.key}>
+ <ContentCell>
<Checkbox
label={translateWithParameters('projects_management.select_project', project.name)}
checked={selected}
onCheck={handleProjectCheck}
/>
- </td>
-
- <td className="nowrap hide-overflow project-row-text-cell">
- <Link
- className="link-no-underline"
- to={getComponentOverviewUrl(project.key, project.qualifier)}
- >
- <QualifierIcon className="little-spacer-right" qualifier={project.qualifier} />
-
+ </ContentCell>
+ <ContentCell className="it__project-row-text-cell">
+ <HoverLink to={getComponentOverviewUrl(project.key, project.qualifier)}>
<Tooltip overlay={project.name} placement="left">
<span>{project.name}</span>
</Tooltip>
- </Link>
+ </HoverLink>
{project.qualifier === ComponentQualifier.Project &&
githubProvisioningEnabled &&
- !project.managed && <span className="badge sw-ml-1">{translate('local')}</span>}
- </td>
-
- <td className="thin nowrap">
+ !project.managed && <Badge className="sw-ml-1">{translate('local')}</Badge>}
+ </ContentCell>
+ <ContentCell>
<PrivacyBadgeContainer qualifier={project.qualifier} visibility={project.visibility} />
- </td>
-
- <td className="nowrap hide-overflow project-row-text-cell">
+ </ContentCell>
+ <ContentCell className="it__project-row-text-cell">
<Tooltip overlay={project.key} placement="left">
- <span className="note">{project.key}</span>
+ <Note>{project.key}</Note>
</Tooltip>
- </td>
-
- <td className="thin nowrap text-right">
+ </ContentCell>
+ <ContentCell>
{project.lastAnalysisDate ? (
<DateFormatter date={project.lastAnalysisDate} />
) : (
- <span className="note">—</span>
+ <Note>—</Note>
)}
- </td>
-
- <td className="thin nowrap">
+ </ContentCell>
+ <ActionCell>
<ProjectRowActions currentUser={currentUser} project={project} />
- </td>
- </tr>
+ </ActionCell>
+ </TableRow>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ActionsDropdown, ItemButton, ItemLink, PopupZLevel, Spinner } from 'design-system';
import React, { useState } from 'react';
import { getComponentNavigation } from '../../api/navigation';
import { Project } from '../../api/project-management';
-import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown';
-import Spinner from '../../components/ui/Spinner';
import { throwGlobalError } from '../../helpers/error';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { getComponentPermissionsUrl } from '../../helpers/urls';
return (
<>
<ActionsDropdown
- label={translateWithParameters('projects_management.show_actions_for_x', project.name)}
+ id="project-management-action-dropdown"
+ toggleClassName="it__user-actions-toggle"
onOpen={handleDropdownOpen}
+ allowResizing
+ ariaLabel={translateWithParameters('projects_management.show_actions_for_x', project.name)}
+ zLevel={PopupZLevel.Global}
>
- {loading ? (
- <ActionsDropdownItem>
- <Spinner />
- </ActionsDropdownItem>
- ) : (
+ <Spinner loading={loading} className="sw-flex sw-ml-3">
<>
{hasAccess === true && (
- <ActionsDropdownItem
- className="js-edit-permissions"
- to={getComponentPermissionsUrl(project.key)}
- >
+ <ItemLink to={getComponentPermissionsUrl(project.key)}>
{translate(project.managed ? 'show_permissions' : 'edit_permissions')}
- </ActionsDropdownItem>
+ </ItemLink>
)}
{hasAccess === false &&
(!project.managed || currentUser.local || !githubProvisioningEnabled) && (
- <ActionsDropdownItem
- className="js-restore-access"
+ <ItemButton
+ className="it__restore-access"
onClick={() => setRestoreAccessModal(true)}
>
{translate('global_permissions.restore_access')}
- </ActionsDropdownItem>
+ </ItemButton>
)}
</>
- )}
+ </Spinner>
{!project.managed && (
- <ActionsDropdownItem
- className="js-apply-template"
- onClick={() => setApplyTemplateModal(true)}
- >
+ <ItemButton className="it__apply-template" onClick={() => setApplyTemplateModal(true)}>
{translate('projects_role.apply_template')}
- </ActionsDropdownItem>
+ </ItemButton>
)}
</ActionsDropdown>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import classNames from 'classnames';
+import { ActionCell, ContentCell, Table, TableRow } from 'design-system';
import * as React from 'react';
import { Project } from '../../api/project-management';
import { translate } from '../../helpers/l10n';
}
};
+ const header = (
+ <TableRow>
+ <ContentCell> </ContentCell>
+ <ContentCell>{translate('name')}</ContentCell>
+ <ContentCell>{translate('visibility')}</ContentCell>
+ <ContentCell>{translate('key')}</ContentCell>
+ <ContentCell>{translate('last_analysis')}</ContentCell>
+ <ActionCell>{translate('actions')}</ActionCell>
+ </TableRow>
+ );
+
return (
- <div className="boxed-group boxed-group-inner">
- <table
- className={classNames('data', 'zebra', { 'new-loading': !ready })}
- id="projects-management-page-projects"
- >
- <thead>
- <tr>
- <th />
- <th>{translate('name')}</th>
- <th />
- <th>{translate('key')}</th>
- <th className="thin nowrap text-right">{translate('last_analysis')}</th>
- <th />
- </tr>
- </thead>
- <tbody>
- {projects.map((project) => (
- <ProjectRow
- currentUser={currentUser}
- key={project.key}
- onProjectCheck={onProjectCheck}
- project={project}
- selected={selection.some((s) => s.key === project.key)}
- />
- ))}
- </tbody>
- </table>
- </div>
+ <Table
+ columnCount={6}
+ header={header}
+ id="projects-management-page-projects"
+ className={classNames({ 'sw-opacity-50 sw-transition sw-duration-75 sw-ease-in': !ready })}
+ >
+ {projects.map((project) => (
+ <ProjectRow
+ currentUser={currentUser}
+ key={project.key}
+ onProjectCheck={onProjectCheck}
+ project={project}
+ selected={selection.some((s) => s.key === project.key)}
+ />
+ ))}
+ </Table>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, Modal } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { grantPermissionToUser } from '../../api/permissions';
import { Project } from '../../api/project-management';
-import Modal from '../../components/controls/Modal';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
import { translate } from '../../helpers/l10n';
import { LoggedInUser } from '../../types/users';
loading: boolean;
}
+const FORM_ID = 'restore-access-form';
+
export default class RestoreAccessModal extends React.PureComponent<Props, State> {
mounted = false;
state: State = { loading: false };
});
render() {
+ const { loading } = this.state;
const header = translate('global_permissions.restore_access');
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <form onSubmit={this.handleFormSubmit}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <div className="modal-body">
+ <Modal
+ headerTitle={header}
+ onClose={this.props.onClose}
+ loading={loading}
+ body={
+ <form id={FORM_ID} onSubmit={this.handleFormSubmit}>
<FormattedMessage
defaultMessage={translate('global_permissions.restore_access.message')}
id="global_permissions.restore_access.message"
administer: <strong>{translate('projects_role.admin')}</strong>,
}}
/>
- </div>
-
- <footer className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <SubmitButton disabled={this.state.loading}>{translate('restore')}</SubmitButton>
- <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
- </footer>
- </form>
- </Modal>
+ </form>
+ }
+ primaryButton={
+ <ButtonPrimary autoFocus disabled={loading} form={FORM_ID} type="submit">
+ {translate('restore')}
+ </ButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import {
+ ButtonSecondary,
+ Checkbox,
+ DangerButtonPrimary,
+ DatePicker,
+ HelperHintIcon,
+ InputSearch,
+ InputSelect,
+ Spinner,
+} from 'design-system';
import { sortBy } from 'lodash';
import * as React from 'react';
-import { components, OptionProps, SingleValueProps } from 'react-select';
+import { OptionProps, SingleValueProps, components } from 'react-select';
import { Project } from '../../api/project-management';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
-import { Button } from '../../components/controls/buttons';
-import Checkbox from '../../components/controls/Checkbox';
-import DateInput from '../../components/controls/DateInput';
import HelpTooltip from '../../components/controls/HelpTooltip';
-import SearchBox from '../../components/controls/SearchBox';
-import Select, { LabelValueSelectOption } from '../../components/controls/Select';
-import QualifierIcon from '../../components/icons/QualifierIcon';
+import { LabelValueSelectOption } from '../../components/controls/Select';
import { translate } from '../../helpers/l10n';
import { AppState } from '../../types/appstate';
import { Visibility } from '../../types/component';
const checked = isAllChecked || thirdState;
return (
<Checkbox
+ className="it__projects-selection"
checked={checked}
id="projects-selection"
onCheck={this.onCheck}
);
};
- renderQualifierOption = (option: LabelValueSelectOption) => (
- <div className="display-flex-center">
- <QualifierIcon className="little-spacer-right" qualifier={option.value} />
- {option.label}
- </div>
- );
+ renderQualifierOption = (option: LabelValueSelectOption) => <div>{option.label}</div>;
renderQualifierFilter = () => {
const options = this.getQualifierOptions();
return null;
}
return (
- <Select
- className="input-medium it__project-qualifier-select"
+ <InputSelect
+ className="it__project-qualifier-select"
isDisabled={!this.props.ready}
name="projects-qualifier"
onChange={this.handleQualifierChange}
{ value: Visibility.Private, label: translate('visibility.private') },
];
return (
- <Select
- className="input-small"
+ <InputSelect
isDisabled={!this.props.ready}
name="projects-visibility"
onChange={this.handleVisibilityChange}
renderTypeFilter = () =>
this.props.qualifiers === 'TRK' ? (
- <div>
+ <div className="sw-flex sw-items-center">
<Checkbox
checked={this.props.provisioned}
- className="link-checkbox-control"
id="projects-provisioned"
onCheck={this.props.onProvisionedChanged}
>
- <span className="text-middle little-spacer-left">
- {translate('provisioning.only_provisioned')}
- </span>
+ <span className="sw-ml-1">{translate('provisioning.only_provisioned')}</span>
<HelpTooltip
- className="spacer-left"
+ className="sw-ml-2"
overlay={translate('provisioning.only_provisioned.tooltip')}
- />
+ >
+ <HelperHintIcon />
+ </HelpTooltip>
</Checkbox>
</div>
) : null;
renderDateFilter = () => {
return (
- <DateInput
- inputClassName="input-medium"
+ <DatePicker
+ clearButtonLabel={translate('clear')}
name="analyzed-before"
onChange={this.props.onDateChanged}
placeholder={translate('last_analysis_before')}
value={this.props.analyzedBefore}
+ showClearButton
+ alignRight
+ size="auto"
/>
);
};
render() {
return (
- <div className="big-spacer-bottom">
- <div className="projects-management-search">
- <div>{this.props.ready ? this.renderCheckbox() : <i className="spinner" />}</div>
+ <div className="sw-mb-4">
+ <div className="sw-flex sw-justify-start sw-items-center sw-flex-wrap sw-gap-2 sw-p-2">
+ <Spinner loading={!this.props.ready} className="sw-ml-2">
+ {this.renderCheckbox()}
+ </Spinner>
{this.renderQualifierFilter()}
{this.renderDateFilter()}
{this.renderVisibilityFilter()}
{this.renderTypeFilter()}
- <div className="flex-grow">
- <SearchBox
+ <div className="sw-flex-grow">
+ <InputSearch
minLength={3}
onChange={this.props.onSearch}
placeholder={translate('search.search_by_name_or_key')}
value={this.props.query}
+ size="auto"
/>
</div>
- <div className="bulk-actions">
- <Button
- className="js-bulk-apply-permission-template"
+ <div>
+ <ButtonSecondary
+ className="it__bulk-apply-permission-template"
disabled={this.props.selection.length === 0}
onClick={this.handleBulkApplyTemplateClick}
>
{translate('permission_templates.bulk_apply_permission_template')}
- </Button>
+ </ButtonSecondary>
{this.props.qualifiers === 'TRK' && (
- <Button
- className="js-delete spacer-left button-red"
+ <DangerButtonPrimary
+ className="sw-ml-2"
disabled={this.props.selection.length === 0}
onClick={this.handleDeleteClick}
title={
}
>
{translate('delete')}
- </Button>
+ </DangerButtonPrimary>
)}
</div>
</div>
}),
projectActions: (projectName: string) =>
byRole('button', { name: `projects_management.show_actions_for_x.${projectName}` }),
- editPermissions: byRole('link', { name: 'edit_permissions' }),
- showPermissions: byRole('link', { name: 'show_permissions' }),
- applyPermissionTemplate: byRole('button', { name: 'projects_role.apply_template' }),
- restoreAccess: byRole('button', { name: 'global_permissions.restore_access' }),
+ editPermissions: byRole('menuitem', { name: 'edit_permissions' }),
+ showPermissions: byRole('menuitem', { name: 'show_permissions' }),
+ applyPermissionTemplate: byRole('menuitem', { name: 'projects_role.apply_template' }),
+ restoreAccess: byRole('menuitem', { name: 'global_permissions.restore_access' }),
editPermissionsPage: byText('/project_roles?id=project1'),
apply: byRole('button', { name: 'apply' }),
qualifierFilter: byRole('combobox', { name: 'projects_management.filter_by_component' }),
analysisDateFilter: byPlaceholderText('last_analysis_before'),
provisionedFilter: byRole('checkbox', {
- name: 'provisioning.only_provisioned help',
+ name: 'provisioning.only_provisioned',
}),
searchFilter: byRole('searchbox', { name: 'search.search_by_name_or_key' }),
.get(),
).toBeInTheDocument();
await selectEvent.select(
- ui.bulkApplyDialog.by(ui.selectTemplate('field_required')).get(),
+ ui.bulkApplyDialog.by(ui.selectTemplate('required')).get(),
'Permission Template 2',
);
await user.click(ui.bulkApplyDialog.by(ui.apply).get());
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-.rdp {
- --rdp-cell-size: 30px;
- --rdp-caption-font-size: 13px;
- /* Ensures the month/year dropdowns do not move on click, but rdp outline is not shown */
- --rdp-outline: 2px solid transparent;
- --rdp-outline-selected: 2px solid transparent;
-}
-
-.rdp-day_selected {
- background-color: var(--blue);
-}
-
-.rdp-day_selected:hover {
- background-color: var(--blue);
-}
-
-.date-input-control {
- position: relative;
- display: inline-block;
- cursor: pointer;
-}
-
-.date-input-control-input {
- width: 130px;
- padding-left: var(--controlHeight) !important;
- cursor: pointer;
-}
-
-.date-input-control-input.is-filled {
- padding-right: 16px !important;
-}
-
-.date-input-control-icon {
- position: absolute;
- top: 4px;
- left: 4px;
-}
-
-.date-input-control-icon path {
- fill: var(--neutral600);
- opacity: 0.9;
-}
-
-.date-input-control-input:focus + .date-input-control-icon path {
- fill: var(--info500);
-}
-
-.date-input-control-reset {
- position: absolute;
- top: 4px;
- right: 4px;
- border: none;
-}
-
-.date-input-calendar {
- position: absolute;
- z-index: var(--dropdownMenuZIndex);
- top: 100%;
- left: 0;
- border: 1px solid var(--barBorderColor);
- background-color: #fff;
- box-shadow: var(--defaultShadow);
-}
-
-.date-input-calendar.align-right {
- left: initial;
- right: 0;
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import { ActiveModifiers, DayPicker, Matcher } from 'react-day-picker';
-import 'react-day-picker/dist/style.css';
-import { injectIntl, WrappedComponentProps } from 'react-intl';
-import { ClearButton } from '../../components/controls/buttons';
-import OutsideClickHandler from '../../components/controls/OutsideClickHandler';
-import CalendarIcon from '../../components/icons/CalendarIcon';
-import { getShortWeekDayName, translate } from '../../helpers/l10n';
-import './DateInput.css';
-import EscKeydownHandler from './EscKeydownHandler';
-import FocusOutHandler from './FocusOutHandler';
-
-// When no minDate is given, year dropdown will show year options up to PAST_MAX_YEARS in the past
-const YEARS_TO_DISPLAY = 10;
-
-interface Props {
- alignRight?: boolean;
- className?: string;
- currentMonth?: Date;
- highlightFrom?: Date;
- highlightTo?: Date;
- inputClassName?: string;
- maxDate?: Date;
- minDate?: Date;
- name?: string;
- id?: string;
- onChange: (date: Date | undefined) => void;
- placeholder: string;
- value?: Date;
-}
-
-interface State {
- currentMonth: Date;
- open: boolean;
- lastHovered?: Date;
-}
-
-export default class DateInput extends React.PureComponent<Props, State> {
- input?: HTMLInputElement | null;
-
- constructor(props: Props) {
- super(props);
-
- this.state = { currentMonth: props.value || props.currentMonth || new Date(), open: false };
- }
-
- focus = () => {
- if (this.input) {
- this.input.focus();
- }
- this.openCalendar();
- };
-
- handleResetClick = () => {
- this.closeCalendar();
- this.props.onChange(undefined);
- };
-
- openCalendar = () => {
- this.setState({
- currentMonth: this.props.value || this.props.currentMonth || new Date(),
- lastHovered: undefined,
- open: true,
- });
- };
-
- closeCalendar = () => {
- this.setState({ open: false });
- };
-
- handleDayClick = (day: Date, modifiers: ActiveModifiers) => {
- if (!modifiers.disabled) {
- this.closeCalendar();
- this.props.onChange(day);
- }
- };
-
- handleDayMouseEnter = (day: Date, modifiers: ActiveModifiers) => {
- this.setState({ lastHovered: modifiers.disabled ? undefined : day });
- };
-
- render() {
- const {
- alignRight,
- highlightFrom,
- highlightTo,
- minDate,
- maxDate = new Date(),
- value: selectedDay,
- name,
- className,
- inputClassName,
- id,
- placeholder,
- } = this.props;
- const { lastHovered, currentMonth, open } = this.state;
-
- // Infer start and end dropdown year from min/max dates, if set
- const fromYear = minDate ? minDate.getFullYear() : new Date().getFullYear() - YEARS_TO_DISPLAY;
- const toYear = maxDate ? maxDate.getFullYear() : new Date().getFullYear() + 1;
-
- let highlighted: Matcher = false;
- const lastHoveredOrValue = lastHovered || selectedDay;
- if (highlightFrom && lastHoveredOrValue) {
- highlighted = { from: highlightFrom, to: lastHoveredOrValue };
- }
- if (highlightTo && lastHoveredOrValue) {
- highlighted = { from: lastHoveredOrValue, to: highlightTo };
- }
-
- return (
- <FocusOutHandler onFocusOut={this.closeCalendar}>
- <OutsideClickHandler onClickOutside={this.closeCalendar}>
- <EscKeydownHandler onKeydown={this.closeCalendar}>
- <span className={classNames('date-input-control', className)}>
- <InputWrapper
- className={classNames('date-input-control-input', inputClassName, {
- 'is-filled': selectedDay !== undefined,
- })}
- id={id}
- innerRef={(node: HTMLInputElement | null) => (this.input = node)}
- name={name}
- onFocus={this.openCalendar}
- placeholder={placeholder}
- readOnly
- type="text"
- value={selectedDay}
- />
- <CalendarIcon className="date-input-control-icon" fill="" />
- {selectedDay !== undefined && (
- <ClearButton
- aria-label={translate('reset_date')}
- className="button-tiny date-input-control-reset"
- iconProps={{ size: 12 }}
- onClick={this.handleResetClick}
- />
- )}
- {open && (
- <div className={classNames('date-input-calendar', { 'align-right': alignRight })}>
- <DayPicker
- mode="default"
- captionLayout="dropdown-buttons"
- fromYear={fromYear}
- toYear={toYear}
- disabled={{ after: maxDate, before: minDate }}
- weekStartsOn={1}
- formatters={{
- formatWeekdayName: (date) => getShortWeekDayName(date.getDay()),
- }}
- modifiers={{ highlighted }}
- modifiersClassNames={{ highlighted: 'highlighted' }}
- month={currentMonth}
- onMonthChange={(currentMonth) => this.setState({ currentMonth })}
- selected={selectedDay}
- onDayClick={this.handleDayClick}
- onDayMouseEnter={this.handleDayMouseEnter}
- />
- </div>
- )}
- </span>
- </EscKeydownHandler>
- </OutsideClickHandler>
- </FocusOutHandler>
- );
- }
-}
-
-type InputWrapperProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value'> &
- WrappedComponentProps & { innerRef: React.Ref<HTMLInputElement>; value: Date | undefined };
-
-const InputWrapper = injectIntl(({ innerRef, intl, value, ...other }: InputWrapperProps) => {
- const formattedValue =
- value && intl.formatDate(value, { year: 'numeric', month: 'short', day: 'numeric' });
- return <input {...other} ref={innerRef} value={formattedValue || ''} />;
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import { max, min } from 'date-fns';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import DateInput from './DateInput';
-
-type DateRange = { from?: Date; to?: Date };
-
-interface Props {
- className?: string;
- maxDate?: Date;
- minDate?: Date;
- onChange: (date: DateRange) => void;
- value?: DateRange;
- alignEndDateCalandarRight?: boolean;
-}
-
-export default class DateRangeInput extends React.PureComponent<Props> {
- toDateInput?: DateInput | null;
-
- get from() {
- return this.props.value && this.props.value.from;
- }
-
- get to() {
- return this.props.value && this.props.value.to;
- }
-
- handleFromChange = (from: Date | undefined) => {
- this.props.onChange({ from, to: this.to });
-
- // use `setTimeout` to work around the immediate closing of the `toDateInput`
- setTimeout(() => {
- if (from && !this.to && this.toDateInput) {
- this.toDateInput.focus();
- }
- });
- };
-
- handleToChange = (to: Date | undefined) => {
- this.props.onChange({ from: this.from, to });
- };
-
- render() {
- const { alignEndDateCalandarRight, minDate, maxDate } = this.props;
-
- return (
- <div className={classNames('display-flex-end', this.props.className)}>
- <div className="display-flex-column">
- <label className="text-bold little-spacer-bottom" htmlFor="date-from">
- {translate('start_date')}
- </label>
- <DateInput
- currentMonth={this.to}
- data-test="from"
- id="date-from"
- highlightTo={this.to}
- minDate={minDate}
- maxDate={maxDate && this.to ? min([maxDate, this.to]) : maxDate || this.to}
- onChange={this.handleFromChange}
- placeholder={translate('start_date')}
- value={this.from}
- />
- </div>
- <span className="note little-spacer-left little-spacer-right little-spacer-bottom">
- {translate('to_')}
- </span>
- <div className="display-flex-column">
- <label className="text-bold little-spacer-bottom" htmlFor="date-to">
- {translate('end_date')}
- </label>
- <DateInput
- alignRight={alignEndDateCalandarRight}
- currentMonth={this.from}
- data-test="to"
- id="date-to"
- highlightFrom={this.from}
- minDate={minDate && this.from ? max([minDate, this.from]) : minDate || this.from}
- maxDate={maxDate}
- onChange={this.handleToChange}
- placeholder={translate('end_date')}
- ref={(element) => (this.toDateInput = element)}
- value={this.to}
- />
- </div>
- </div>
- );
- }
-}