Merge pull request #10394 from mabashian/brand-name

Pull brand name from default.strings.json

SUMMARY
We were pulling this string from variables.js.
There were several places where the brand name was not being pulled dynamically - this PR addresses that.  This changes the way we set the document title a little bit.  Since we needed access to a dynamic string, I went down the JS route to set this.

ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

UI

Reviewed-by: Jake McDermott <yo@jakemcdermott.me>
Reviewed-by: Sarah Akus <sarah.akus@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot]
2021-06-09 23:07:37 +00:00
committed by GitHub
27 changed files with 4020 additions and 3103 deletions
-1
View File
@@ -37,7 +37,6 @@
name="description"
content="AWX"
/>
<title>AWX</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
+13 -3
View File
@@ -12,7 +12,6 @@ import { ErrorBoundary } from 'react-error-boundary';
import { I18nProvider } from '@lingui/react';
import { i18n } from '@lingui/core';
import { Card, PageSection } from '@patternfly/react-core';
import { ConfigProvider, useAuthorizedPath } from './contexts/Config';
import { SessionProvider, useSession } from './contexts/Session';
import AppContainer from './components/AppContainer';
@@ -20,15 +19,14 @@ import Background from './components/Background';
import ContentError from './components/ContentError';
import NotFound from './screens/NotFound';
import Login from './screens/Login';
import { isAuthenticated } from './util/auth';
import { getLanguageWithoutRegionCode } from './util/language';
import { dynamicActivate, locales } from './i18nLoader';
import Metrics from './screens/Metrics';
import getRouteConfig from './routeConfig';
import SubscriptionEdit from './screens/Setting/Subscription/SubscriptionEdit';
import { SESSION_REDIRECT_URL } from './constants';
import { RootAPI } from './api';
function ErrorFallback({ error }) {
return (
@@ -113,10 +111,22 @@ function App() {
// preferred language, default to one that has strings.
language = 'en';
}
useEffect(() => {
dynamicActivate(language);
}, [language]);
useEffect(() => {
async function fetchBrandName() {
const {
data: { BRAND_NAME },
} = await RootAPI.readAssetVariables();
document.title = BRAND_NAME;
}
fetchBrandName();
}, []);
const redirectURL = window.sessionStorage.getItem(SESSION_REDIRECT_URL);
if (redirectURL) {
window.sessionStorage.removeItem(SESSION_REDIRECT_URL);
+9
View File
@@ -1,12 +1,21 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../testUtils/enzymeHelpers';
import { RootAPI } from './api';
import * as SessionContext from './contexts/Session';
import App from './App';
jest.mock('./api');
describe('<App />', () => {
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
});
test('renders ok', async () => {
const contextValues = {
setAuthRedirectTo: jest.fn(),
+4 -4
View File
@@ -2,12 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { t } from '@lingui/macro';
import { AboutModal } from '@patternfly/react-core';
import { BrandName } from '../../variables';
import useBrandName from '../../util/useBrandName';
function About({ version, isOpen, onClose }) {
const brandName = useBrandName();
const createSpeechBubble = () => {
let text = `${BrandName} ${version}`;
let text = `${brandName.current} ${version}`;
let top = '';
let bottom = '';
@@ -31,7 +31,7 @@ function About({ version, isOpen, onClose }) {
<AboutModal
isOpen={isOpen}
onClose={onClose}
productName={`Ansible ${BrandName}`}
productName={`Ansible ${brandName.current}`}
trademark={`${copyright} ${new Date().getFullYear()} ${redHatInc}`}
brandImageSrc="/static/media/logo-header.svg"
brandImageAlt={t`Brand Image`}
@@ -9,6 +9,7 @@ import {
InventoriesAPI,
CredentialsAPI,
ExecutionEnvironmentsAPI,
RootAPI,
} from '../../api';
import AdHocCommands from './AdHocCommands';
@@ -16,6 +17,7 @@ jest.mock('../../api/models/CredentialTypes');
jest.mock('../../api/models/Inventories');
jest.mock('../../api/models/Credentials');
jest.mock('../../api/models/ExecutionEnvironments');
jest.mock('../../api/models/Root');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -40,6 +42,11 @@ const adHocItems = [
describe('<AdHocCommands />', () => {
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
InventoriesAPI.readAdHocOptions.mockResolvedValue({
data: {
actions: {
@@ -4,13 +4,14 @@ import {
mountWithContexts,
waitForElement,
} from '../../../testUtils/enzymeHelpers';
import { CredentialsAPI, ExecutionEnvironmentsAPI } from '../../api';
import { CredentialsAPI, ExecutionEnvironmentsAPI, RootAPI } from '../../api';
import AdHocCommandsWizard from './AdHocCommandsWizard';
jest.mock('../../api/models/CredentialTypes');
jest.mock('../../api/models/Inventories');
jest.mock('../../api/models/Credentials');
jest.mock('../../api/models/ExecutionEnvironments');
jest.mock('../../api/models/Root');
const verbosityOptions = [
{ value: '0', key: '0', label: '0 (Normal)' },
@@ -32,6 +33,11 @@ describe('<AdHocCommandsWizard/>', () => {
let wrapper;
const onLaunch = jest.fn();
beforeEach(async () => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
await act(async () => {
wrapper = mountWithContexts(
<AdHocCommandsWizard
@@ -6,8 +6,6 @@ import PropTypes from 'prop-types';
import { useField } from 'formik';
import { Form, FormGroup, Switch, Checkbox } from '@patternfly/react-core';
import styled from 'styled-components';
import { BrandName } from '../../variables';
import AnsibleSelect from '../AnsibleSelect';
import FormField from '../FormField';
import { VariablesField } from '../CodeEditor';
@@ -18,17 +16,14 @@ import {
} from '../FormLayout';
import Popover from '../Popover';
import { required } from '../../util/validators';
import useBrandName from '../../util/useBrandName';
const TooltipWrapper = styled.div`
text-align: left;
`;
// Setting BrandName to a variable here is necessary to get the jest tests
// passing. Attempting to use BrandName in the template literal results
// in failing tests.
const brandName = BrandName;
function AdHocDetailsStep({ verbosityOptions, moduleOptions }) {
const brandName = useBrandName();
const [moduleNameField, moduleNameMeta, moduleNameHelpers] = useField({
name: 'module_name',
validate: required(null),
@@ -70,7 +65,7 @@ function AdHocDetailsStep({ verbosityOptions, moduleOptions }) {
}
labelIcon={
<Popover
content={t`These are the modules that ${brandName} supports running commands against.`}
content={t`These are the modules that ${brandName.current} supports running commands against.`}
/>
}
>
@@ -2,9 +2,11 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { Formik } from 'formik';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import { RootAPI } from '../../api';
import DetailsStep from './AdHocDetailsStep';
jest.mock('../../api/models/Credentials');
jest.mock('../../api/models/Root');
const verbosityOptions = [
{ key: -1, value: '', label: '', isDisabled: false },
@@ -32,6 +34,14 @@ const initialValues = {
describe('<AdHocDetailsStep />', () => {
let wrapper;
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
});
test('should mount properly', async () => {
await act(async () => {
wrapper = mountWithContexts(
-3
View File
@@ -3,9 +3,6 @@ import ReactDOM from 'react-dom';
import './setupCSP';
import '@patternfly/react-core/dist/styles/base.css';
import App from './App';
import { BrandName } from './variables';
document.title = `${BrandName}`;
ReactDOM.render(
<React.StrictMode>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -6,7 +6,7 @@ import {
waitForElement,
} from '../../../../testUtils/enzymeHelpers';
import ProjectEdit from './ProjectEdit';
import { ProjectsAPI, CredentialTypesAPI } from '../../../api';
import { ProjectsAPI, CredentialTypesAPI, RootAPI } from '../../../api';
jest.mock('../../../api');
@@ -83,6 +83,11 @@ describe('<ProjectEdit />', () => {
};
beforeEach(async () => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
await ProjectsAPI.readOptions.mockImplementation(
() => projectOptionsResolve
);
@@ -5,7 +5,7 @@ import {
waitForElement,
} from '../../../../testUtils/enzymeHelpers';
import ProjectForm from './ProjectForm';
import { CredentialTypesAPI, ProjectsAPI } from '../../../api';
import { CredentialTypesAPI, ProjectsAPI, RootAPI } from '../../../api';
jest.mock('../../../api');
@@ -81,6 +81,11 @@ describe('<ProjectForm />', () => {
};
beforeEach(async () => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
await ProjectsAPI.readOptions.mockImplementation(
() => projectOptionsResolve
);
@@ -1,6 +1,5 @@
import 'styled-components/macro';
import React from 'react';
import { t } from '@lingui/macro';
import { useField } from 'formik';
import { FormGroup, Alert } from '@patternfly/react-core';
@@ -8,18 +7,14 @@ import { required } from '../../../../util/validators';
import AnsibleSelect from '../../../../components/AnsibleSelect';
import FormField from '../../../../components/FormField';
import Popover from '../../../../components/Popover';
import { BrandName } from '../../../../variables';
// Setting BrandName to a variable here is necessary to get the jest tests
// passing. Attempting to use BrandName in the template literal results
// in failing tests.
const brandName = BrandName;
import useBrandName from '../../../../util/useBrandName';
const ManualSubForm = ({
localPath,
project_base_dir,
project_local_paths,
}) => {
const brandName = useBrandName();
const localPaths = [...new Set([...project_local_paths, localPath])];
const options = [
{
@@ -54,7 +49,7 @@ const ManualSubForm = ({
Either that directory is empty, or all of the contents are already
assigned to other projects. Create a new directory there and make
sure the playbook files can be read by the "awx" system user,
or have ${brandName} directly retrieve your playbooks from
or have ${brandName.current} directly retrieve your playbooks from
source control using the Source Control Type option above.`}
</Alert>
)}
@@ -1,6 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { t } from '@lingui/macro';
import {
Card as _Card,
@@ -14,14 +13,9 @@ import {
PageSection,
} from '@patternfly/react-core';
import styled from 'styled-components';
import { BrandName } from '../../variables';
import { useConfig } from '../../contexts/Config';
import ContentLoading from '../../components/ContentLoading/ContentLoading';
// Setting BrandName to a variable here is necessary to get the jest tests
// passing. Attempting to use BrandName in the template literal results
// in failing tests.
const brandName = BrandName;
import useBrandName from '../../util/useBrandName';
const SplitLayout = styled(PageSection)`
column-count: 1;
@@ -50,10 +44,11 @@ const CardDescription = styled.div`
function SettingList() {
const config = useConfig();
const brandName = useBrandName();
const settingRoutes = [
{
header: t`Authentication`,
description: t`Enable simplified login for your ${brandName} applications`,
description: t`Enable simplified login for your ${brandName.current} applications`,
id: 'authentication',
routes: [
{
@@ -88,7 +83,7 @@ function SettingList() {
},
{
header: t`Jobs`,
description: t`Update settings pertaining to Jobs within ${brandName}`,
description: t`Update settings pertaining to Jobs within ${brandName.current}`,
id: 'jobs',
routes: [
{
@@ -5,7 +5,7 @@ import {
mountWithContexts,
waitForElement,
} from '../../../testUtils/enzymeHelpers';
import { SettingsAPI } from '../../api';
import { SettingsAPI, RootAPI } from '../../api';
import mockAllOptions from './shared/data.allSettingOptions.json';
import Settings from './Settings';
@@ -19,6 +19,11 @@ describe('<Settings />', () => {
let wrapper;
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
SettingsAPI.readAllOptions.mockResolvedValue({
data: mockAllOptions,
});
@@ -12,6 +12,7 @@ import {
JobTemplatesAPI,
LabelsAPI,
ProjectsAPI,
RootAPI,
} from '../../../api';
jest.mock('../../../api');
@@ -67,6 +68,11 @@ describe('<JobTemplateAdd />', () => {
};
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
CredentialsAPI.read.mockResolvedValue({
data: {
results: [],
@@ -14,6 +14,7 @@ import {
InventoriesAPI,
ExecutionEnvironmentsAPI,
InstanceGroupsAPI,
RootAPI,
} from '../../../api';
import JobTemplateEdit from './JobTemplateEdit';
import useDebounce from '../../../util/useDebounce';
@@ -27,6 +28,7 @@ jest.mock('../../../api/models/Projects');
jest.mock('../../../api/models/Inventories');
jest.mock('../../../api/models/ExecutionEnvironments');
jest.mock('../../../api/models/InstanceGroups');
jest.mock('../../../api/models/Root');
const mockJobTemplate = {
allow_callbacks: false,
@@ -203,6 +205,11 @@ const mockExecutionEnvironment = [
describe('<JobTemplateEdit />', () => {
beforeEach(() => {
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
JobTemplatesAPI.readCredentials.mockResolvedValue({
data: mockRelatedCredentials,
});
@@ -13,11 +13,10 @@ import {
} from '@patternfly/react-core';
import ContentError from '../../../components/ContentError';
import ContentLoading from '../../../components/ContentLoading';
import { BrandName } from '../../../variables';
import AnsibleSelect from '../../../components/AnsibleSelect';
import { TagMultiSelect } from '../../../components/MultiSelect';
import useRequest from '../../../util/useRequest';
import useBrandName from '../../../util/useBrandName';
import FormActionGroup from '../../../components/FormActionGroup';
import FormField, {
CheckboxField,
@@ -67,6 +66,7 @@ function JobTemplateForm({
Boolean(template.webhook_service)
);
const isMounted = useIsMounted();
const brandName = useBrandName();
const [askInventoryOnLaunchField] = useField('ask_inventory_on_launch');
const [jobTypeField, jobTypeMeta, jobTypeHelpers] = useField({
@@ -567,7 +567,7 @@ function JobTemplateForm({
&nbsp;
<Popover
content={t`Enables creation of a provisioning
callback URL. Using the URL a host can contact ${BrandName}
callback URL. Using the URL a host can contact ${brandName.current}
and request a configuration update using this job
template.`}
/>
@@ -15,6 +15,7 @@ import {
CredentialsAPI,
CredentialTypesAPI,
InventoriesAPI,
RootAPI,
} from '../../../api';
jest.mock('../../../api');
@@ -95,6 +96,11 @@ describe('<JobTemplateForm />', () => {
beforeEach(() => {
consoleError = global.console.error;
global.console.error = jest.fn();
RootAPI.readAssetVariables.mockResolvedValue({
data: {
BRAND_NAME: 'AWX',
},
});
LabelsAPI.read.mockReturnValue({
data: mockData.summary_fields.labels,
});
+19
View File
@@ -0,0 +1,19 @@
import { useEffect, useRef } from 'react';
import { RootAPI } from '../api';
export default function useBrandName() {
const brandName = useRef('');
useEffect(() => {
async function fetchBrandName() {
const {
data: { BRAND_NAME },
} = await RootAPI.readAssetVariables();
brandName.current = BRAND_NAME;
}
fetchBrandName();
}, []);
return brandName;
}
-2
View File
@@ -1,2 +0,0 @@
export const BrandName = 'AWX';
export const PendoAPIKey = '';