Skip to content

Commit

Permalink
Adds popover help text to project details, and unifies those strings …
Browse files Browse the repository at this point in the history
…(used in the form and the details view) into 1 file (#12039)
  • Loading branch information
AlexSCorey committed Apr 19, 2022
1 parent 3a1268d commit ae7960e
Show file tree
Hide file tree
Showing 14 changed files with 356 additions and 264 deletions.
5 changes: 3 additions & 2 deletions awx/ui/src/components/DetailList/Detail.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { node, bool, string } from 'prop-types';

import { oneOfType, node, bool, string } from 'prop-types';
import { TextListItem, TextListItemVariants } from '@patternfly/react-core';
import styled from 'styled-components';
import Popover from '../Popover';
Expand Down Expand Up @@ -81,7 +82,7 @@ Detail.propTypes = {
value: node,
fullWidth: bool,
alwaysVisible: bool,
helpText: string,
helpText: oneOfType([string, node]),
};
Detail.defaultProps = {
value: null,
Expand Down
2 changes: 2 additions & 0 deletions awx/ui/src/components/Popover/Popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const PopoverButton = styled.button`
padding: var(--pf-global--spacer--xs);
margin: -(var(--pf-global--spacer--xs));
font-size: var(--pf-global--FontSize--sm);
--pf-c-form__group-label-help--Color: var(--pf-global--Color--200);
--pf-c-form__group-label-help--hover--Color: var(--pf-global--Color--100);
`;

function Popover({ ariaLabel, content, header, id, maxWidth, ...rest }) {
Expand Down
82 changes: 60 additions & 22 deletions awx/ui/src/screens/Project/ProjectDetail/ProjectDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import useRequest, { useDismissableError } from 'hooks/useRequest';
import { relatedResourceDeleteRequests } from 'util/getRelatedResourceDeleteDetails';
import StatusLabel from 'components/StatusLabel';
import { formatDateString } from 'util/dates';
import Popover from 'components/Popover';
import ProjectSyncButton from '../shared/ProjectSyncButton';
import ProjectHelpTextStrings from '../shared/Project.helptext';
import useWsProject from './useWsProject';

const Label = styled.span`
Expand Down Expand Up @@ -57,7 +59,7 @@ function ProjectDetail({ project }) {
summary_fields,
} = useWsProject(project);
const history = useHistory();

const projectHelpText = ProjectHelpTextStrings();
const {
request: deleteProject,
isLoading,
Expand All @@ -82,29 +84,37 @@ function ProjectDetail({ project }) {
optionsList = (
<TextList component={TextListVariants.ul}>
{scm_clean && (
<TextListItem
component={TextListItemVariants.li}
>{t`Discard local changes before syncing`}</TextListItem>
<TextListItem component={TextListItemVariants.li}>
{t`Discard local changes before syncing`}
<Popover content={projectHelpText.options.clean} />
</TextListItem>
)}
{scm_delete_on_update && (
<TextListItem
component={TextListItemVariants.li}
>{t`Delete the project before syncing`}</TextListItem>
<TextListItem component={TextListItemVariants.li}>
{t`Delete the project before syncing`}{' '}
<Popover
content={projectHelpText.options.delete}
id="scm-delete-on-update"
/>
</TextListItem>
)}
{scm_track_submodules && (
<TextListItem
component={TextListItemVariants.li}
>{t`Track submodules latest commit on branch`}</TextListItem>
<TextListItem component={TextListItemVariants.li}>
{t`Track submodules latest commit on branch`}{' '}
<Popover content={projectHelpText.options.trackSubModules} />
</TextListItem>
)}
{scm_update_on_launch && (
<TextListItem
component={TextListItemVariants.li}
>{t`Update revision on job launch`}</TextListItem>
<TextListItem component={TextListItemVariants.li}>
{t`Update revision on job launch`}{' '}
<Popover content={projectHelpText.options.updateOnLaunch} />
</TextListItem>
)}
{allow_override && (
<TextListItem
component={TextListItemVariants.li}
>{t`Allow branch override`}</TextListItem>
<TextListItem component={TextListItemVariants.li}>
{t`Allow branch override`}{' '}
<Popover content={projectHelpText.options.allowBranchOverride} />
</TextListItem>
)}
</TextList>
);
Expand Down Expand Up @@ -134,7 +144,10 @@ function ProjectDetail({ project }) {
} else if (summary_fields?.last_job) {
job = summary_fields.last_job;
}

const getSourceControlUrlHelpText = () =>
scm_type === 'git'
? projectHelpText.githubSourceControlUrl
: projectHelpText.svnSourceControlUrl;
return (
<CardBody>
<DetailList gutter="sm">
Expand Down Expand Up @@ -197,9 +210,25 @@ function ProjectDetail({ project }) {
}
alwaysVisible
/>
<Detail label={t`Source Control URL`} value={scm_url} />
<Detail label={t`Source Control Branch`} value={scm_branch} />
<Detail label={t`Source Control Refspec`} value={scm_refspec} />
<Detail
helpText={
scm_type === 'git' || scm_type === 'svn'
? getSourceControlUrlHelpText()
: ''
}
label={t`Source Control URL`}
value={scm_url}
/>
<Detail
helpText={projectHelpText.branchFormField}
label={t`Source Control Branch`}
value={scm_branch}
/>
<Detail
helpText={projectHelpText.sourceControlRefspec}
label={t`Source Control Refspec`}
value={scm_refspec}
/>
{summary_fields.credential && (
<Detail
label={t`Source Control Credential`}
Expand All @@ -217,16 +246,25 @@ function ProjectDetail({ project }) {
value={`${scm_update_cache_timeout} ${t`Seconds`}`}
/>
<ExecutionEnvironmentDetail
helpText={projectHelpText.executionEnvironment}
virtualEnvironment={custom_virtualenv}
executionEnvironment={summary_fields?.default_environment}
isDefaultEnvironment
/>
<Config>
{({ project_base_dir }) => (
<Detail label={t`Project Base Path`} value={project_base_dir} />
<Detail
helpText={projectHelpText.projectBasePath}
label={t`Project Base Path`}
value={project_base_dir}
/>
)}
</Config>
<Detail label={t`Playbook Directory`} value={local_path} />
<Detail
helpText={projectHelpText.projectLocalPath}
label={t`Playbook Directory`}
value={local_path}
/>
<UserDateDetail
label={t`Created`}
date={created}
Expand Down
30 changes: 19 additions & 11 deletions awx/ui/src/screens/Project/ProjectDetail/ProjectDetail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ jest.mock('react-router-dom', () => ({
url: '/projects/1/details',
}),
}));
jest.mock('hooks/useBrandName', () => ({
__esModule: true,
default: () => ({
current: 'AWX',
}),
}));
describe('<ProjectDetail />', () => {
const mockProject = {
id: 1,
Expand Down Expand Up @@ -126,16 +132,18 @@ describe('<ProjectDetail />', () => {
'2019-10-10T01:15:06.780490Z'
);
expect(
wrapper
.find('Detail[label="Enabled Options"]')
.containsAllMatchingElements([
<li>Discard local changes before syncing</li>,
<li>Delete the project before syncing</li>,
<li>Track submodules latest commit on branch</li>,
<li>Update revision on job launch</li>,
<li>Allow branch override</li>,
])
).toEqual(true);
wrapper.find('Detail[label="Enabled Options"]').find('li')
).toHaveLength(5);
const options = [
'Discard local changes before syncing',
'Delete the project before syncing',
'Track submodules latest commit on branch',
'Update revision on job launch',
'Allow branch override',
];
wrapper.find('li').map((item, index) => {
expect(item.text().includes(options[index]));
});
});

test('should hide options label when all project options return false', () => {
Expand Down Expand Up @@ -237,7 +245,7 @@ describe('<ProjectDetail />', () => {
expect(history.location.pathname).toEqual('/projects/1/edit');
});

test('sync button should call api to syn project', async () => {
test('sync button should call api to sync project', async () => {
ProjectsAPI.readSync.mockResolvedValue({ data: { can_update: true } });
const wrapper = mountWithContexts(<ProjectDetail project={mockProject} />);
await act(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
import ProjectsListItem from './ProjectListItem';

jest.mock('../../../api/models/Projects');

jest.mock('hooks/useBrandName', () => ({
__esModule: true,
default: () => ({
current: 'AWX',
}),
}));
describe('<ProjectsListItem />', () => {
test('launch button shown to users with start capabilities', () => {
const wrapper = mountWithContexts(
Expand Down
142 changes: 142 additions & 0 deletions awx/ui/src/screens/Project/shared/Project.helptext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from 'react';
import { t } from '@lingui/macro';
import getDocsBaseUrl from 'util/getDocsBaseUrl';
import { useConfig } from 'contexts/Config';
import useBrandName from 'hooks/useBrandName';

const ProjectHelpTextStrings = () => ({
executionEnvironment: t`The execution environment that will be used for jobs that use this project. This will be used as fallback when an execution environment has not been explicitly assigned at the job template or workflow level.`,
projectBasePath: (
<span>
{t`Base path used for locating playbooks. Directories
found inside this path will be listed in the playbook directory drop-down.
Together the base path and selected playbook directory provide the full
path used to locate playbooks.`}
<br />
<br />
{t`Change PROJECTS_ROOT when deploying
${useBrandName()} to change this location.`}
</span>
),
projectLocalPath: t`Select from the list of directories found in
the Project Base Path. Together the base path and the playbook
directory provide the full path used to locate playbooks.`,
githubSourceControlUrl: (
<span>
{t`Example URLs for GIT Source Control include:`}
<ul css="margin: 10px 0 10px 20px">
<li>
<code>https://github.com/ansible/ansible.git</code>
</li>
<li>
<code>git@github.com:ansible/ansible.git</code>
</li>
<li>
<code>git://servername.example.com/ansible.git</code>
</li>
</ul>
{t`Note: When using SSH protocol for GitHub or
Bitbucket, enter an SSH key only, do not enter a username
(other than git). Additionally, GitHub and Bitbucket do
not support password authentication when using SSH. GIT
read only protocol (git://) does not use username or
password information.`}
</span>
),
svnSourceControlUrl: (
<span>
{t`Example URLs for Subversion Source Control include:`}
<ul css={{ margin: '10px 0 10px 20px' }}>
<li>
<code>https://github.com/ansible/ansible</code>
</li>
<li>
<code>svn://servername.example.com/path</code>
</li>
<li>
<code>svn+ssh://servername.example.com/path</code>
</li>
</ul>
</span>
),
syncButtonDisabled: t`This project is currently on sync and cannot be clicked until sync process completed`,
archiveUrl: (
<span>
{t`Example URLs for Remote Archive Source Control include:`}
<ul css={{ margin: '10px 0 10px 20px' }}>
<li>
<code>https://github.com/username/project/archive/v0.0.1.tar.gz</code>
</li>
<li>
<code>https://github.com/username/project/archive/v0.0.2.zip</code>
</li>
</ul>
</span>
),

sourceControlRefspec: (
<span>
{t`A refspec to fetch (passed to the Ansible git
module). This parameter allows access to references via
the branch field not otherwise available.`}
<br />
<br />
{t`Note: This field assumes the remote name is "origin".`}
<br />
<br />
{t`Examples include:`}
<ul css={{ margin: '10px 0 10px 20px' }}>
<li>
<code>refs/*:refs/remotes/origin/*</code>
</li>
<li>
<code>refs/pull/62/head:refs/remotes/origin/pull/62/head</code>
</li>
</ul>
{t`The first fetches all references. The second
fetches the Github pull request number 62, in this example
the branch needs to be "pull/62/head".`}
<br />
<br />
{t`For more information, refer to the`}{' '}
<a
target="_blank"
rel="noopener noreferrer"
href={`${getDocsBaseUrl(
useConfig()
)}/html/userguide/projects.html#manage-playbooks-using-source-control`}
>
{t`Documentation.`}
</a>
</span>
),
branchFormField: t`Branch to checkout. In addition to branches,
you can input tags, commit hashes, and arbitrary refs. Some
commit hashes and refs may not be available unless you also
provide a custom refspec.`,
options: {
clean: t`Remove any local modifications prior to performing an update.`,
delete: t`Delete the local repository in its entirety prior to
performing an update. Depending on the size of the
repository this may significantly increase the amount
of time required to complete an update.`,
trackSubModules: t`Submodules will track the latest commit on
their master branch (or other branch specified in
.gitmodules). If no, submodules will be kept at
the revision specified by the main project.
This is equivalent to specifying the --remote
flag to git submodule update.`,
updateOnLaunch: t`Each time a job runs using this project, update the
revision of the project prior to starting the job.`,
allowBranchOverride: t`Allow changing the Source Control branch or revision in a job
template that uses this project.`,
cacheTimeout: t`Time in seconds to consider a project
to be current. During job runs and callbacks the task
system will evaluate the timestamp of the latest project
update. If it is older than Cache Timeout, it is not
considered current, and a new project update will be
performed.`,
},
});

export default ProjectHelpTextStrings;
3 changes: 2 additions & 1 deletion awx/ui/src/screens/Project/shared/ProjectForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ExecutionEnvironmentLookup from 'components/Lookup/ExecutionEnvironmentLo
import { CredentialTypesAPI, ProjectsAPI } from 'api';
import { required } from 'util/validators';
import { FormColumnLayout, SubFormLayout } from 'components/FormLayout';
import ProjectHelpTextStrings from './Project.helptext';
import {
GitSubForm,
SvnSubForm,
Expand Down Expand Up @@ -195,7 +196,7 @@ function ProjectFormFields({
}
onBlur={() => executionEnvironmentHelpers.setTouched()}
value={executionEnvironmentField.value}
popoverContent={t`The execution environment that will be used for jobs that use this project. This will be used as fallback when an execution environment has not been explicitly assigned at the job template or workflow level.`}
popoverContent={ProjectHelpTextStrings.execution_environment}
onChange={handleExecutionEnvironmentUpdate}
tooltip={t`Select an organization before editing the default execution environment.`}
globallyAvailable
Expand Down
Loading

0 comments on commit ae7960e

Please sign in to comment.