foreman_theme_satellite 16.4.2 → 16.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54d89d60344083fcf31950dec0172c89165a903d4a1fe3ab06bf8eeaa3b88840
4
- data.tar.gz: 2ddc9af30171ffc58074c56b064b595294145142f79f4034ec7110f6239c71e0
3
+ metadata.gz: 0f4492cd1ca5ab22e180b6767695d8a7d7d029915018c5214dd257dfbc1009c8
4
+ data.tar.gz: 593d0c39829ac5c5f2bd889c23baa151d0e824da8257510eea8238bf935f833b
5
5
  SHA512:
6
- metadata.gz: 332f7a55cbb67c6a4733cb34e44d14b803a1265b00fad689c9101e7b6b6ee4f7b5fee5e11286e2d6d7833271cf8a9dc834050fd32a698c524311ad1da1f53b77
7
- data.tar.gz: 6ff2275c254713a345914fddd26138053ed6b5b4b855f75ddcbe2266480746646753324cb67c916efb7c0be0c1d163a04d4b7ec87003d781e8817f62c6a1da0c
6
+ metadata.gz: 6e4089ace8320b932eb4e1a49dca2ee34e61125284a2e996b54f6621568337f9e863342350cc31b5caaf09c531b7ff0398e07ca3ee1a5d8330eb8ea2c02c31fe
7
+ data.tar.gz: 1fdfde63ecab76fc18516aaf11fab2d6808426b5594526caaa8c9313ba8292dfaccc0137db517892b6445220dc0141fc3839b8632673ff71167b95a87eda93ca
data/README.md CHANGED
@@ -73,11 +73,18 @@ that will be branded automatically to
73
73
  Many times we need to use a downstream link instead of an upstream one. The most
74
74
  common example for this is pointing to a documentation link.
75
75
 
76
- All links to documentation in Foreman are local and redirected to
77
- `links_controller`. This controller is prepended with
78
- `documentation_controller_branding` concern that uses dictionary stored in
79
- `lib/foreman_theme_satellite/documentation.rb` to redirect documentation to
80
- downstream URLs.
76
+ Documentation controls are hidden by this theme. Inline documentation links remain
77
+ visible, and the `manual`, `docs`, `plugin_manual`, `wiki`, and upgrade documentation
78
+ routes redirect to the configured Satellite version's landing page:
79
+ `{base}/en/documentation/red_hat_satellite/{version}`. The base comes from the
80
+ `satellite_documentation_url` setting, including offline documentation servers.
81
+ Direct Satellite documentation links in the page are rewritten to `/links/manual`
82
+ (with the application's URL prefix). Support and the upgrade helper keep their
83
+ existing destinations.
84
+
85
+ The detailed mappings in `lib/foreman_theme_satellite/documentation.rb` are retained
86
+ for future restoration, but these redirects currently bypass them. The mapping
87
+ examples below describe that retained mechanism, not the active redirect behavior.
81
88
 
82
89
  In foreman:
83
90
  ``` ruby
@@ -0,0 +1,12 @@
1
+ // Hide identified standalone controls only. Do not match hrefs: welcome
2
+ // pages use those same /links destinations inside sentences.
3
+ .satellite-documentation-hidden,
4
+ .docs-element-to-remove,
5
+ [data-ouia-component-id="doc-url-dropdown"],
6
+ [data-ouia-component-id="action-buttons-documentation"],
7
+ [data-ouia-component-id="register-host-documentation-button"],
8
+ [data-ouia-component-id="audits-documentation-button"],
9
+ [data-ouia-component-id="inventory-documentation-button"],
10
+ [data-ouia-component-id="upgrade-docs-button"] {
11
+ display: none !important;
12
+ }
@@ -1 +1,2 @@
1
1
  //= require foreman_theme_satellite/theme
2
+ //= require foreman_theme_satellite/documentation
@@ -3,6 +3,19 @@ require_relative '../../../lib/foreman_theme_satellite/documentation'
3
3
  module DocumentationControllerBranding
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ # Keep the detailed mappings below for future restoration. All public
7
+ # documentation redirects currently land on the configured version's index.
8
+ def external_url(type:, options: {})
9
+ documentation_redirect = %w[manual docs plugin_manual wiki].include?(type.to_s)
10
+ upgrade_documentation_redirect = type.to_s == 'upgrade' && options[:section] == 'documentation'
11
+
12
+ if documentation_redirect || upgrade_documentation_redirect
13
+ documentation_root_url
14
+ else
15
+ super
16
+ end
17
+ end
18
+
6
19
  def documentation_url(section = nil, options = {})
7
20
  url = ''
8
21
  unless section.nil?
@@ -55,12 +68,10 @@ module DocumentationControllerBranding
55
68
  "#{unversioned_documentation_root}/#{ForemanThemeSatellite.documentation_version}"
56
69
  end
57
70
 
58
- def documentation_root
59
- "#{unversioned_documentation_root}/#{ForemanThemeSatellite.documentation_version}/html-single"
60
- end
71
+ alias_method :documentation_root, :documentation_root_url
61
72
 
62
73
  def unversioned_documentation_root
63
- "#{Setting[:satellite_documentation_url]}/documentation/en-us/red_hat_satellite"
74
+ "#{Setting[:satellite_documentation_url]}/en/documentation/red_hat_satellite"
64
75
  end
65
76
 
66
77
  private
@@ -0,0 +1,30 @@
1
+ # Hide Rails documentation controls without changing their URL helpers or redirects.
2
+ module DocumentationVisibilityHelper
3
+ DOCUMENTATION_URL_TO_REMOVE = '#satellite-documentation-link-to-remove'.freeze
4
+ DOCUMENTATION_ELEMENT_TO_REMOVE_CLASS = 'docs-element-to-remove'.freeze
5
+ DOCUMENTATION_SECTIONS_TO_REMOVE = %w[
6
+ 4.1.1LDAPAuthentication
7
+ 5.7ExternalAuthentication
8
+ ].freeze
9
+
10
+ def help_button
11
+ ''
12
+ end
13
+
14
+ def documentation_button(*_args, **_options)
15
+ ''
16
+ end
17
+
18
+ def documentation_url(section = nil, *args, **options)
19
+ return DOCUMENTATION_URL_TO_REMOVE if DOCUMENTATION_SECTIONS_TO_REMOVE.include?(section.to_s)
20
+
21
+ super
22
+ end
23
+
24
+ def link_to(name = nil, options = nil, html_options = nil, &block)
25
+ link_options = block_given? ? name : options
26
+ return tag.div(class: DOCUMENTATION_ELEMENT_TO_REMOVE_CLASS) if link_options == DOCUMENTATION_URL_TO_REMOVE
27
+
28
+ super
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'foreman_puppet/config_groups/welcome',
3
+ name: 'satellite_hide_config_groups_documentation',
4
+ replace: "erb[loud]:contains('EmptyState')",
5
+ text: <<~ERB
6
+ <%= react_component('EmptyState', {
7
+ :icon => 'object-group',
8
+ :iconType => 'fa',
9
+ :header => _('Config Groups'),
10
+ :description => description,
11
+ :action => action
12
+ }) %>
13
+ ERB
14
+ )
15
+
16
+ Deface::Override.new(
17
+ virtual_path: 'foreman_puppet/environments/welcome',
18
+ name: 'satellite_hide_environments_documentation',
19
+ replace: "erb[loud]:contains('WelcomeEnv')",
20
+ text: <<~ERB
21
+ <%= react_component('SatelliteWelcomeEnv', {canCreate: authorized_for(action: :new)}) %>
22
+ ERB
23
+ )
@@ -102,6 +102,7 @@ module ForemanThemeSatellite
102
102
 
103
103
  UINotifications::StringParser.send :prepend, DeprecationNotification::StringParser
104
104
  Notification.singleton_class.send :prepend, DeprecationNotification::Notification
105
+ ApplicationHelper.prepend DocumentationVisibilityHelper
105
106
  LinksController.prepend DocumentationControllerBranding
106
107
  ProvisioningTemplatesController.include ProvisioningTemplatesControllerBranding
107
108
  ProvisioningTemplatesHelper.prepend ProvisioningTemplatesHelperBranding
@@ -1,3 +1,3 @@
1
1
  module ForemanThemeSatellite
2
- VERSION = '16.4.2'.freeze
2
+ VERSION = '16.4.3'.freeze
3
3
  end
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import { foremanUrl, getManualURL } from 'foremanReact/common/helpers';
6
+ import EmptyState from 'foremanReact/components/common/EmptyState';
7
+
8
+ export const SatelliteWelcomeEnv = ({ canCreate }) => {
9
+ const action = canCreate && {
10
+ title: __('Create Puppet Environment'),
11
+ url: foremanUrl('/foreman_puppet/environments/new'),
12
+ };
13
+
14
+ const description = (
15
+ <FormattedMessage
16
+ id="puppetenv-welcome"
17
+ defaultMessage={__(
18
+ 'If you are planning to use Foreman as an external node classifier you should provide information about one or more environments.{newLine}This information is commonly imported from a pre-existing Puppet configuration by the use of the {puppetClassesLinkToDocs} and environment importer.'
19
+ )}
20
+ values={{
21
+ newLine: <br />,
22
+ puppetClassesLinkToDocs: (
23
+ <a
24
+ target="_blank"
25
+ href={getManualURL('4.2.2Classes')}
26
+ rel="noreferrer"
27
+ >
28
+ {__('Puppet classes')}
29
+ </a>
30
+ ),
31
+ }}
32
+ />
33
+ );
34
+
35
+ return (
36
+ <EmptyState
37
+ icon="th"
38
+ iconType="fa"
39
+ header={__('Puppet Environments')}
40
+ description={description}
41
+ action={action}
42
+ />
43
+ );
44
+ };
45
+
46
+ SatelliteWelcomeEnv.propTypes = {
47
+ canCreate: PropTypes.bool,
48
+ };
49
+
50
+ SatelliteWelcomeEnv.defaultProps = {
51
+ canCreate: false,
52
+ };
53
+
54
+ export default SatelliteWelcomeEnv;
@@ -1,6 +1,12 @@
1
1
  import React from 'react';
2
2
 
3
- import { Card, CardBody, CardTitle, CardFooter, Button } from '@patternfly/react-core';
3
+ import {
4
+ Card,
5
+ CardBody,
6
+ CardTitle,
7
+ CardFooter,
8
+ Button,
9
+ } from '@patternfly/react-core';
4
10
  import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
5
11
 
6
12
  import { translate as __ } from 'foremanReact/common/I18n';
@@ -10,8 +16,10 @@ const SatelliteUpgradeHelperCard = () => (
10
16
  <Card ouiaId="upgrade-docs-satellite-helper" isFlat>
11
17
  <CardTitle component="h4">{__('Satellite upgrade helper')}</CardTitle>
12
18
  <CardBody>
13
- {__('Generate a customized upgrade plan based on your current Satellite environment.' +
14
- ' This tool will guide you through the upgrade process and provide additional steps to avoid known issues specific to your upgrade scenario.')}
19
+ {__(
20
+ 'Generate a customized upgrade plan based on your current Satellite environment.' +
21
+ ' This tool will guide you through the upgrade process and provide additional steps to avoid known issues specific to your upgrade scenario.'
22
+ )}
15
23
  </CardBody>
16
24
  <CardFooter>
17
25
  <Button
@@ -0,0 +1,89 @@
1
+ // PatternFly's legacy dropdown keeps references to CSS-hidden items. Handle
2
+ // navigation in affected menus before its handler attempts to focus those refs.
3
+ const navigationKeys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
4
+ const openingKeys = [...navigationKeys, 'Enter', ' '];
5
+ const dropdownSelector = '.pf-v5-c-dropdown';
6
+ const menuSelector = '.pf-v5-c-dropdown__menu';
7
+ const itemSelector = '[role="menuitem"]';
8
+
9
+ const isVisible = node => {
10
+ const { defaultView } = node.ownerDocument;
11
+ for (let ancestor = node; ancestor; ancestor = ancestor.parentElement) {
12
+ const style = defaultView.getComputedStyle(ancestor);
13
+ if (
14
+ ancestor.hidden ||
15
+ style.display === 'none' ||
16
+ style.visibility === 'hidden'
17
+ ) {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ };
23
+
24
+ export default function installMenuKeyboard(doc, isDocumentationControl) {
25
+ const frames = new Set();
26
+ const navigate = (dropdown, key, current) => {
27
+ const menu = dropdown.querySelector(menuSelector);
28
+ if (!menu || !isVisible(menu)) return false;
29
+ const items = [...menu.querySelectorAll(itemSelector)];
30
+ if (!items.some(isDocumentationControl)) return false;
31
+ const available = items.filter(
32
+ item =>
33
+ !isDocumentationControl(item) &&
34
+ !item.hasAttribute('disabled') &&
35
+ item.getAttribute('aria-disabled') !== 'true' &&
36
+ isVisible(item)
37
+ );
38
+ const lastIndex = available.length - 1;
39
+ const currentIndex = available.indexOf(current);
40
+ let nextIndex = 0;
41
+ if (key === 'End' || (key === 'ArrowUp' && currentIndex <= 0)) {
42
+ nextIndex = lastIndex;
43
+ } else if (key === 'ArrowUp') {
44
+ nextIndex = currentIndex - 1;
45
+ } else if (key === 'ArrowDown' && currentIndex >= 0) {
46
+ nextIndex = (currentIndex + 1) % available.length;
47
+ }
48
+ const target =
49
+ available[nextIndex] || dropdown.querySelector('[aria-expanded]');
50
+ if (target) target.focus();
51
+ return true;
52
+ };
53
+
54
+ const handleEvent = event => {
55
+ const dropdown = event.target.closest(dropdownSelector);
56
+ if (!dropdown) return;
57
+ const toggle = event.target.closest('[aria-expanded]');
58
+ const key = event.type === 'click' ? 'Enter' : event.key;
59
+ if (event.type === 'keydown' && navigationKeys.includes(key)) {
60
+ const current = event.target.closest(itemSelector);
61
+ if (navigate(dropdown, key, current)) {
62
+ event.preventDefault();
63
+ event.stopImmediatePropagation();
64
+ return;
65
+ }
66
+ }
67
+ if (!toggle || !openingKeys.includes(key)) return;
68
+ // Let React open the menu and PatternFly finish its initial focus first.
69
+ // This also handles menus whose first item is hidden documentation.
70
+ const frame = doc.defaultView.requestAnimationFrame(() => {
71
+ frames.delete(frame);
72
+ if (
73
+ toggle.isConnected &&
74
+ toggle.getAttribute('aria-expanded') === 'true'
75
+ ) {
76
+ navigate(dropdown, key, null);
77
+ }
78
+ });
79
+ frames.add(frame);
80
+ };
81
+
82
+ doc.addEventListener('keydown', handleEvent, true);
83
+ doc.addEventListener('click', handleEvent, true);
84
+ return () => {
85
+ doc.removeEventListener('keydown', handleEvent, true);
86
+ doc.removeEventListener('click', handleEvent, true);
87
+ frames.forEach(frame => doc.defaultView.cancelAnimationFrame(frame));
88
+ };
89
+ }
@@ -0,0 +1,229 @@
1
+ import React, { useState } from 'react';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import {
5
+ render,
6
+ fireEvent,
7
+ screen,
8
+ waitFor,
9
+ cleanup,
10
+ } from '@testing-library/react';
11
+ import '@testing-library/jest-dom';
12
+ import {
13
+ Dropdown,
14
+ DropdownItem,
15
+ DropdownToggle,
16
+ DropdownGroup,
17
+ } from '@patternfly/react-core/deprecated';
18
+ import { ActionButtons } from 'foremanReact/components/PF4/TableIndexPage/ActionButtons';
19
+ import DocumentationLink from 'foremanReact/components/PF4/DocumentationLink';
20
+ import { installDocumentationHiding } from './hideDocumentation';
21
+
22
+ const styles = fs
23
+ .readFileSync(
24
+ path.join(
25
+ __dirname,
26
+ '../../app/assets/stylesheets/foreman_theme_satellite/documentation.scss'
27
+ ),
28
+ 'utf8'
29
+ )
30
+ .replace(/^\/\/.*$/gm, '');
31
+ let installation;
32
+
33
+ beforeEach(() => {
34
+ const style = document.createElement('style');
35
+ style.textContent = styles;
36
+ style.id = 'documentation-test-style';
37
+ document.head.appendChild(style);
38
+ installation = installDocumentationHiding(document);
39
+ });
40
+ afterEach(() => {
41
+ installation.disconnect();
42
+ cleanup();
43
+ document.getElementById('documentation-test-style').remove();
44
+ });
45
+
46
+ // Use the same grouped PatternFly menu and documentation component as bookmarks.
47
+ const BookmarksMenu = () => {
48
+ const [isOpen, setOpen] = useState(false);
49
+ return (
50
+ <Dropdown
51
+ ouiaId="test-bookmarks"
52
+ isOpen={isOpen}
53
+ isGrouped
54
+ toggle={
55
+ <DropdownToggle onToggle={(_event, open) => setOpen(open)}>
56
+ Bookmarks
57
+ </DropdownToggle>
58
+ }
59
+ >
60
+ <DropdownGroup>
61
+ <DocumentationLink href="/links/manual" />
62
+ <DropdownItem ouiaId="saved-bookmark" href="/hosts?search=saved">
63
+ Saved bookmark
64
+ </DropdownItem>
65
+ <DropdownItem ouiaId="disabled-bookmark" isDisabled>
66
+ Unavailable bookmark
67
+ </DropdownItem>
68
+ <DropdownItem ouiaId="manage-bookmarks">Manage bookmarks</DropdownItem>
69
+ </DropdownGroup>
70
+ </Dropdown>
71
+ );
72
+ };
73
+
74
+ const press = (element, key) =>
75
+ fireEvent.keyDown(element, {
76
+ key,
77
+ keyCode: {
78
+ ArrowDown: 40,
79
+ ArrowUp: 38,
80
+ Home: 36,
81
+ End: 35,
82
+ Enter: 13,
83
+ Escape: 27,
84
+ }[key],
85
+ });
86
+
87
+ test('table dropdown skips documentation and wraps between functional actions', async () => {
88
+ const refresh = jest.fn();
89
+ render(
90
+ <ActionButtons
91
+ buttons={[
92
+ { title: 'Create', action: { href: '/hosts/new' } },
93
+ { title: 'Export', action: { href: '/hosts.csv' } },
94
+ { title: 'Documentation', action: { href: '/hosts/help' } },
95
+ { title: 'Refresh', action: { onClick: refresh } },
96
+ ]}
97
+ />
98
+ );
99
+ const toggle = screen.getByRole('button', { name: 'toggle action dropdown' });
100
+ toggle.focus();
101
+ press(toggle, 'Enter');
102
+ const first = await screen.findByRole('menuitem', { name: 'Export' });
103
+ const last = screen.getByRole('menuitem', { name: 'Refresh' });
104
+ await waitFor(() => expect(first).toHaveFocus());
105
+ await waitFor(() =>
106
+ expect(
107
+ screen.queryByRole('menuitem', { name: 'Documentation' })
108
+ ).not.toBeInTheDocument()
109
+ );
110
+ press(first, 'ArrowDown');
111
+ expect(last).toHaveFocus();
112
+ press(last, 'ArrowDown');
113
+ expect(first).toHaveFocus();
114
+ press(first, 'ArrowUp');
115
+ expect(last).toHaveFocus();
116
+ press(last, 'Home');
117
+ expect(first).toHaveFocus();
118
+ press(first, 'End');
119
+ expect(last).toHaveFocus();
120
+ press(last, 'Enter');
121
+ expect(refresh).toHaveBeenCalledTimes(1);
122
+ press(last, 'Escape');
123
+ await waitFor(() => expect(toggle).toHaveFocus());
124
+ expect(toggle).toHaveAttribute('aria-expanded', 'false');
125
+ });
126
+
127
+ test.each(['Enter', ' '])(
128
+ 'opening with %s skips a hidden first item',
129
+ async (key) => {
130
+ render(<BookmarksMenu />);
131
+ const toggle = screen.getByRole('button', { name: 'Bookmarks' });
132
+ toggle.focus();
133
+ press(toggle, key);
134
+ const first = await screen.findByRole('menuitem', {
135
+ name: 'Saved bookmark',
136
+ });
137
+ await waitFor(() => expect(first).toHaveFocus());
138
+ expect(
139
+ screen.queryByRole('menuitem', { name: 'Documentation' })
140
+ ).not.toBeInTheDocument();
141
+ press(first, 'ArrowDown');
142
+ expect(
143
+ screen.getByRole('menuitem', { name: 'Manage bookmarks' })
144
+ ).toHaveFocus();
145
+ press(document.activeElement, 'ArrowDown');
146
+ expect(first).toHaveFocus();
147
+ }
148
+ );
149
+
150
+ test('ArrowUp from the open toggle focuses the last available action', async () => {
151
+ render(<BookmarksMenu />);
152
+ const toggle = screen.getByRole('button', { name: 'Bookmarks' });
153
+ toggle.focus();
154
+ fireEvent.click(toggle);
155
+ await waitFor(() =>
156
+ expect(
157
+ screen.getByRole('menuitem', { name: 'Saved bookmark' })
158
+ ).toHaveFocus()
159
+ );
160
+ toggle.focus();
161
+ press(toggle, 'ArrowUp');
162
+ const last = await screen.findByRole('menuitem', {
163
+ name: 'Manage bookmarks',
164
+ });
165
+ await waitFor(() => expect(last).toHaveFocus());
166
+ });
167
+
168
+ test('mouse opening also avoids a hidden initial focus target', async () => {
169
+ render(<BookmarksMenu />);
170
+ fireEvent.click(screen.getByRole('button', { name: 'Bookmarks' }));
171
+ await waitFor(() =>
172
+ expect(
173
+ screen.getByRole('menuitem', { name: 'Saved bookmark' })
174
+ ).toHaveFocus()
175
+ );
176
+ });
177
+
178
+ test('menus without documentation retain native keyboard handling', async () => {
179
+ render(
180
+ <ActionButtons
181
+ buttons={[
182
+ { title: 'Create', action: {} },
183
+ { title: 'Export', action: { href: '/hosts.csv' } },
184
+ { title: 'Refresh', action: {} },
185
+ ]}
186
+ />
187
+ );
188
+ const toggle = screen.getByRole('button', { name: 'toggle action dropdown' });
189
+ fireEvent.click(toggle);
190
+ const first = await screen.findByRole('menuitem', { name: 'Export' });
191
+ first.focus();
192
+ press(first, 'ArrowDown');
193
+ expect(screen.getByRole('menuitem', { name: 'Refresh' })).toHaveFocus();
194
+ });
195
+
196
+ test('disconnect removes the theme keyboard listener', async () => {
197
+ render(<BookmarksMenu />);
198
+ fireEvent.click(screen.getByRole('button', { name: 'Bookmarks' }));
199
+ const first = await screen.findByRole('menuitem', { name: 'Saved bookmark' });
200
+ await waitFor(() => expect(first).toHaveFocus());
201
+ installation.disconnect();
202
+ const bubble = jest.fn();
203
+ document.addEventListener('keydown', bubble);
204
+ press(first, 'Home');
205
+ expect(bubble).toHaveBeenCalled();
206
+ document.removeEventListener('keydown', bubble);
207
+ });
208
+
209
+ test('a documentation-only menu keeps focus on its toggle and can close', async () => {
210
+ render(
211
+ <ActionButtons
212
+ buttons={[
213
+ { title: 'Create', action: {} },
214
+ { title: 'Documentation', action: { href: '/hosts/help' } },
215
+ ]}
216
+ />
217
+ );
218
+ const toggle = screen.getByRole('button', { name: 'toggle action dropdown' });
219
+ toggle.focus();
220
+ press(toggle, 'Enter');
221
+ await waitFor(() => expect(toggle).toHaveAttribute('aria-expanded', 'true'));
222
+ await waitFor(() =>
223
+ expect(screen.queryByRole('menuitem')).not.toBeInTheDocument()
224
+ );
225
+ press(toggle, 'ArrowDown');
226
+ expect(toggle).toHaveFocus();
227
+ press(toggle, 'Escape');
228
+ expect(toggle).toHaveAttribute('aria-expanded', 'false');
229
+ });
@@ -0,0 +1,173 @@
1
+ import installMenuKeyboard from './documentationMenuKeyboard';
2
+
3
+ // Hide only identified documentation buttons and menu actions. Leave inline
4
+ // links and their surrounding text intact; never remove nodes owned by React.
5
+ export const hiddenClass = 'satellite-documentation-hidden';
6
+
7
+ const controlIds = [
8
+ 'doc-url-dropdown',
9
+ 'action-buttons-documentation',
10
+ 'register-host-documentation-button',
11
+ 'audits-documentation-button',
12
+ 'inventory-documentation-button',
13
+ 'upgrade-docs-button',
14
+ ];
15
+
16
+ const controlIdSet = new Set(controlIds);
17
+
18
+ const controlSelector = controlIds
19
+ .map(id => `[data-ouia-component-id="${id}"]`)
20
+ .join(',');
21
+
22
+ const dropdownItemSelector =
23
+ '[data-ouia-component-id="action-buttons-dropdown"] [data-ouia-component-id]';
24
+
25
+ const httpHref = /^https?:\/\//i;
26
+
27
+ const satelliteDocsPattern = /\/(?:[a-z]{2}(?:-[a-z]{2})?\/documentation|documentation\/[a-z]{2}(?:-[a-z]{2})?)\/red_hat_satellite(?:\/|$)/i;
28
+
29
+ const isDocumentationControl = (node, documentationLabel) => {
30
+ const id = node.getAttribute('data-ouia-component-id');
31
+ if (!id) return false;
32
+ if (controlIdSet.has(id)) return true;
33
+ return (
34
+ id === `${documentationLabel}-dropdown-item` &&
35
+ node.closest('[data-ouia-component-id="action-buttons-dropdown"]') !== null
36
+ );
37
+ };
38
+
39
+ const syncDocumentationControl = (node, documentationLabel) => {
40
+ if (!node || node.nodeType !== 1) return;
41
+ if (isDocumentationControl(node, documentationLabel)) {
42
+ node.classList.add(hiddenClass);
43
+ return;
44
+ }
45
+ if (node.classList.contains(hiddenClass)) node.classList.remove(hiddenClass);
46
+ };
47
+
48
+ const hideDocumentationIn = (root, documentationLabel) => {
49
+ if (!root || root.nodeType !== 1) return;
50
+ syncDocumentationControl(root, documentationLabel);
51
+ root.querySelectorAll(controlSelector).forEach(node => {
52
+ node.classList.add(hiddenClass);
53
+ });
54
+ const itemId = `${documentationLabel}-dropdown-item`;
55
+ root.querySelectorAll(dropdownItemSelector).forEach(node => {
56
+ if (node.getAttribute('data-ouia-component-id') === itemId) {
57
+ node.classList.add(hiddenClass);
58
+ }
59
+ });
60
+ };
61
+
62
+ export const hideDocumentation = (
63
+ doc,
64
+ documentationLabel = 'Documentation'
65
+ ) => {
66
+ // Recalculate so a React node reused for a functional link becomes visible.
67
+ doc
68
+ .querySelectorAll(`.${hiddenClass}`)
69
+ .forEach(node => node.classList.remove(hiddenClass));
70
+ hideDocumentationIn(doc.documentElement || doc, documentationLabel);
71
+ };
72
+
73
+ const normalizeLink = (link, landingPath) => {
74
+ const href = link.getAttribute('href');
75
+ // A rewrite sets href to the landing path and the observer hears that
76
+ // write. Matching it here makes the follow-up a no-op.
77
+ if (!href || href === landingPath || !httpHref.test(href)) return;
78
+ let url;
79
+ try {
80
+ url = new URL(href);
81
+ } catch (error) {
82
+ return;
83
+ }
84
+ if (satelliteDocsPattern.test(url.pathname)) {
85
+ link.setAttribute('href', landingPath);
86
+ }
87
+ };
88
+
89
+ const normalizeIn = (root, landingPath) => {
90
+ if (!root || root.nodeType !== 1) return;
91
+ if (typeof root.matches === 'function' && root.matches('a[href]')) {
92
+ normalizeLink(root, landingPath);
93
+ }
94
+ root.querySelectorAll('a[href]').forEach(link => {
95
+ normalizeLink(link, landingPath);
96
+ });
97
+ };
98
+
99
+ // Direct Satellite documentation links bypass LinksController. Send them
100
+ // through its landing redirect too, including links in stored notifications.
101
+ export const normalizeSatelliteDocumentation = (doc, landingPath) => {
102
+ normalizeIn(doc.documentElement || doc, landingPath);
103
+ };
104
+
105
+ export const installDocumentationHiding = (
106
+ doc,
107
+ documentationLabel = 'Documentation',
108
+ landingPath
109
+ ) => {
110
+ hideDocumentation(doc, documentationLabel);
111
+ if (landingPath) normalizeSatelliteDocumentation(doc, landingPath);
112
+
113
+ const removeMenuKeyboard = installMenuKeyboard(doc, node =>
114
+ isDocumentationControl(node, documentationLabel)
115
+ );
116
+ const pending = [];
117
+ let scheduled = false;
118
+ const flush = () => {
119
+ scheduled = false;
120
+ const batch = pending.splice(0);
121
+ batch.forEach(change => {
122
+ if (change.type === 'childList') {
123
+ change.added.forEach(node => {
124
+ hideDocumentationIn(node, documentationLabel);
125
+ if (landingPath) normalizeIn(node, landingPath);
126
+ });
127
+ return;
128
+ }
129
+ if (change.name === 'href') {
130
+ if (
131
+ landingPath &&
132
+ change.target.nodeType === 1 &&
133
+ change.target.matches('a[href]')
134
+ ) {
135
+ normalizeLink(change.target, landingPath);
136
+ }
137
+ return;
138
+ }
139
+ syncDocumentationControl(change.target, documentationLabel);
140
+ });
141
+ };
142
+
143
+ const observer = new doc.defaultView.MutationObserver(mutations => {
144
+ mutations.forEach(mutation => {
145
+ if (mutation.type === 'childList') {
146
+ if (mutation.addedNodes.length === 0) return;
147
+ pending.push({ type: 'childList', added: [...mutation.addedNodes] });
148
+ return;
149
+ }
150
+ pending.push({
151
+ type: 'attributes',
152
+ name: mutation.attributeName,
153
+ target: mutation.target,
154
+ });
155
+ });
156
+ if (scheduled || pending.length === 0) return;
157
+ scheduled = true;
158
+ doc.defaultView.requestAnimationFrame(flush);
159
+ });
160
+ observer.observe(doc.body, {
161
+ subtree: true,
162
+ childList: true,
163
+ attributes: true,
164
+ attributeFilter: ['data-ouia-component-id', 'href'],
165
+ });
166
+ return {
167
+ disconnect: () => {
168
+ observer.disconnect();
169
+ pending.length = 0;
170
+ removeMenuKeyboard();
171
+ },
172
+ };
173
+ };
@@ -0,0 +1,294 @@
1
+ import assert from 'assert';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import {
5
+ hideDocumentation,
6
+ hiddenClass,
7
+ normalizeSatelliteDocumentation,
8
+ installDocumentationHiding,
9
+ } from './hideDocumentation';
10
+
11
+ const styles = fs
12
+ .readFileSync(
13
+ path.join(
14
+ __dirname,
15
+ '../../app/assets/stylesheets/foreman_theme_satellite/documentation.scss'
16
+ ),
17
+ 'utf8'
18
+ )
19
+ .replace(/^\/\/.*$/gm, '');
20
+ const createDOM = (html) => {
21
+ document.head.innerHTML = `<style>${styles}</style>`;
22
+ document.body.innerHTML = html;
23
+ };
24
+ afterEach(() => {
25
+ document.head.innerHTML = '';
26
+ document.body.innerHTML = '';
27
+ jest.restoreAllMocks();
28
+ });
29
+
30
+ test('hides documentation controls but preserves inline links, sentences and actions', async () => {
31
+ createDOM(
32
+ `<div class="foreman-empty-state"><div><div class="empty-state-description">Description</div><span id="sentence">See <a href="/links/manual">documentation</a></span></div><ul id="permissions"><li>view_hosts</li></ul><a id="create" href="/hosts/new">Create</a></div><ul><li id="docs"><button data-ouia-component-id="doc-url-dropdown">Docs</button></li><li id="support"><a href="/links/support">Support</a></li></ul>`
33
+ );
34
+ const doc = document;
35
+ hideDocumentation(doc);
36
+ assert(doc.querySelector('#docs button').classList.contains(hiddenClass));
37
+ assert(!doc.getElementById('docs').classList.contains(hiddenClass));
38
+ ['sentence', 'permissions', 'create', 'support'].forEach((id) => {
39
+ assert(!doc.getElementById(id).classList.contains(hiddenClass));
40
+ });
41
+ assert.equal(
42
+ doc.querySelector('#sentence a').getAttribute('href'),
43
+ '/links/manual'
44
+ );
45
+ });
46
+
47
+ test('handles React node reuse without leaving functional links hidden', async () => {
48
+ createDOM(
49
+ '<a data-ouia-component-id="audits-documentation-button" href="/links/manual">Documentation</a>'
50
+ );
51
+ const doc = document;
52
+ hideDocumentation(doc);
53
+ const link = doc.querySelector('a');
54
+ assert(link.classList.contains(hiddenClass));
55
+ link.setAttribute('data-ouia-component-id', 'create-host');
56
+ link.href = '/hosts/new';
57
+ hideDocumentation(doc);
58
+ assert(!link.classList.contains(hiddenClass));
59
+ });
60
+
61
+ test('observes late documentation buttons and reused control identifiers', async () => {
62
+ createDOM('<main></main>');
63
+ const doc = document;
64
+ const observer = installDocumentationHiding(doc);
65
+ const link = doc.createElement('a');
66
+ link.href = '/links/docs/Managing_Hosts';
67
+ link.setAttribute(
68
+ 'data-ouia-component-id',
69
+ 'register-host-documentation-button'
70
+ );
71
+ doc.body.appendChild(link);
72
+ await new Promise((resolve) => setTimeout(resolve, 60));
73
+ assert(link.classList.contains(hiddenClass));
74
+ link.setAttribute('data-ouia-component-id', 'create-host');
75
+ link.href = '/hosts/new';
76
+ await new Promise((resolve) => setTimeout(resolve, 60));
77
+ assert(!link.classList.contains(hiddenClass));
78
+ observer.disconnect();
79
+ });
80
+
81
+ test('hides localized table documentation without hiding other dropdown actions', async () => {
82
+ createDOM(
83
+ '<div data-ouia-component-id="action-buttons-dropdown"><ul><li id="docs"><a data-ouia-component-id="Dokumentace-dropdown-item" href="/hosts/help">Dokumentace</a></li><li id="export"><a data-ouia-component-id="Export-dropdown-item" href="/hosts.csv">Export</a></li></ul></div>'
84
+ );
85
+ hideDocumentation(document, 'Dokumentace');
86
+ assert(document.querySelector('#docs a').classList.contains(hiddenClass));
87
+ assert(!document.getElementById('export').classList.contains(hiddenClass));
88
+ });
89
+
90
+ test('welcome sentences keep their documentation links', async () => {
91
+ assert.doesNotMatch(styles, /a\[href/);
92
+ createDOM(`
93
+ <div class="pf-v5-c-empty-state__body">
94
+ <div class="empty-state-description">No discovered hosts found in this context.</div>
95
+ <span id="discovered">For more information please see <a href="/links/plugin_manual?name=foreman_discovery">documentation</a></span>
96
+ </div>
97
+ <div class="empty-state-description" id="reports">If you wish to configure Puppet to forward its reports to Foreman, please follow <a href="/links/manual/3.5.4PuppetReports">setting up reporting</a> and <a href="/links/wiki/Mail_Notifications">e-mail reporting</a></div>
98
+ `);
99
+ const doc = document;
100
+ hideDocumentation(doc, 'Documentation');
101
+ normalizeSatelliteDocumentation(doc, '/links/manual');
102
+ assert.equal(
103
+ doc.querySelector('#discovered').textContent,
104
+ 'For more information please see documentation'
105
+ );
106
+ assert.equal(
107
+ doc.querySelector('#reports').textContent,
108
+ 'If you wish to configure Puppet to forward its reports to Foreman, please follow setting up reporting and e-mail reporting'
109
+ );
110
+ doc.querySelectorAll('#discovered a, #reports a').forEach((link) => {
111
+ assert.notEqual(window.getComputedStyle(link).display, 'none');
112
+ });
113
+ });
114
+
115
+ test('CSS and JavaScript leave inline documentation links visible', async () => {
116
+ const urls = [
117
+ '/links/manual',
118
+ '/links/docs/Managing_Hosts',
119
+ '/links/plugin_manual',
120
+ '/links/wiki',
121
+ 'https://docs.redhat.com/en/documentation',
122
+ 'https://docs.theforeman.org/',
123
+ ];
124
+ createDOM(
125
+ urls.map((url) => `<p>See <a href="${url}">documentation</a>.</p>`).join('')
126
+ );
127
+ hideDocumentation(document);
128
+ document.querySelectorAll('a').forEach((link) => {
129
+ assert.notEqual(window.getComputedStyle(link).display, 'none');
130
+ assert.notEqual(
131
+ window.getComputedStyle(link.parentElement).display,
132
+ 'none'
133
+ );
134
+ });
135
+ });
136
+
137
+ test('hides only the upgrade documentation button and preserves card content and helper', async () => {
138
+ createDOM(
139
+ '<section id="card"><p id="description">Upgrade instructions</p><a data-ouia-component-id="upgrade-docs-button" href="/links/upgrade?section=documentation">Documentation</a><a id="helper" data-ouia-component-id="upgrade-helper-button" href="/links/upgrade?section=helper">Upgrade helper</a></section>'
140
+ );
141
+ hideDocumentation(document);
142
+ const style = (selector) =>
143
+ window.getComputedStyle(document.querySelector(selector)).display;
144
+ assert.equal(style('[data-ouia-component-id="upgrade-docs-button"]'), 'none');
145
+ ['#card', '#description', '#helper'].forEach((selector) => {
146
+ assert.notEqual(style(selector), 'none');
147
+ });
148
+ });
149
+
150
+ test('direct Satellite links use the landing redirect while other products and support remain unchanged', async () => {
151
+ const satelliteUrls = [
152
+ 'https://docs.redhat.com/en/documentation/red_hat_satellite/6.19/managing_hosts/index#chapter',
153
+ 'https://access.redhat.com/documentation/en-us/red_hat_satellite/6.10/html/guide/chapter',
154
+ 'https://offline.example/docs/en/documentation/red_hat_satellite/6.19/guide',
155
+ ];
156
+ const preserved = [
157
+ 'https://docs.redhat.com/en/documentation/subscription_central/1/guide',
158
+ 'https://docs.ansible.com/ansible/latest/guide',
159
+ 'https://access.redhat.com/products/red-hat-satellite#get-support',
160
+ 'https://access.redhat.com/labs/satelliteupgradehelper',
161
+ 'https://access.redhat.com/solutions/satellite6-tasks#task',
162
+ 'https://access.redhat.com/articles/1586183',
163
+ ];
164
+ createDOM(
165
+ [...satelliteUrls, ...preserved]
166
+ .map((url) => `<a href="${url}">Documentation</a>`)
167
+ .join('')
168
+ );
169
+ normalizeSatelliteDocumentation(document, '/prefix/links/manual');
170
+ const links = [...document.querySelectorAll('a')];
171
+ links.slice(0, satelliteUrls.length).forEach((link) => {
172
+ assert.equal(link.getAttribute('href'), '/prefix/links/manual');
173
+ assert.notEqual(window.getComputedStyle(link).display, 'none');
174
+ });
175
+ links
176
+ .slice(satelliteUrls.length)
177
+ .forEach((link, i) => assert.equal(link.href, preserved[i]));
178
+ });
179
+
180
+ test('normalizes late Satellite links and React href updates', async () => {
181
+ createDOM('<main></main>');
182
+ const observer = installDocumentationHiding(
183
+ document,
184
+ 'Documentation',
185
+ '/prefix/links/manual'
186
+ );
187
+ const link = document.createElement('a');
188
+ link.href =
189
+ 'https://docs.redhat.com/en/documentation/red_hat_satellite/6.19/guide';
190
+ document.body.appendChild(link);
191
+ await new Promise((resolve) => setTimeout(resolve, 60));
192
+ assert.equal(link.getAttribute('href'), '/prefix/links/manual');
193
+ link.href =
194
+ 'https://docs.redhat.com/en/documentation/red_hat_satellite/6.19/another-guide';
195
+ await new Promise((resolve) => setTimeout(resolve, 60));
196
+ assert.equal(link.getAttribute('href'), '/prefix/links/manual');
197
+ observer.disconnect();
198
+ });
199
+
200
+ test('observer passes visit added subtrees and changed attributes only', async () => {
201
+ createDOM(
202
+ '<a id="outside" href="https://docs.ansible.com/ansible/latest/guide">Ansible</a><button id="kept" data-ouia-component-id="doc-url-dropdown">Docs</button><main></main>'
203
+ );
204
+ const doc = document;
205
+ const observer = installDocumentationHiding(
206
+ doc,
207
+ 'Dokumentace',
208
+ '/prefix/links/manual'
209
+ );
210
+ const documentQueries = [];
211
+ const querySelectorAll = doc.querySelectorAll.bind(doc);
212
+ jest.spyOn(doc, 'querySelectorAll').mockImplementation((selector) => {
213
+ documentQueries.push(selector);
214
+ return querySelectorAll(selector);
215
+ });
216
+ const wrapper = doc.createElement('div');
217
+ wrapper.innerHTML =
218
+ '<div data-ouia-component-id="action-buttons-dropdown"><a id="localized" data-ouia-component-id="Dokumentace-dropdown-item" href="/hosts/help">Dokumentace</a></div><a id="inside" href="https://docs.redhat.com/en/documentation/red_hat_satellite/6.19/guide">Docs</a><button id="audits" data-ouia-component-id="audits-documentation-button">Documentation</button>';
219
+ doc.querySelector('main').appendChild(wrapper);
220
+ await new Promise((resolve) => setTimeout(resolve, 60));
221
+ assert.equal(documentQueries.length, 0);
222
+ assert.equal(
223
+ doc.getElementById('inside').getAttribute('href'),
224
+ '/prefix/links/manual'
225
+ );
226
+ assert.equal(
227
+ doc.getElementById('outside').getAttribute('href'),
228
+ 'https://docs.ansible.com/ansible/latest/guide'
229
+ );
230
+ assert(doc.getElementById('localized').classList.contains(hiddenClass));
231
+ assert(doc.getElementById('audits').classList.contains(hiddenClass));
232
+ assert(doc.getElementById('kept').classList.contains(hiddenClass));
233
+ doc
234
+ .getElementById('localized')
235
+ .setAttribute('data-ouia-component-id', 'Export-dropdown-item');
236
+ await new Promise((resolve) => setTimeout(resolve, 60));
237
+ assert.equal(documentQueries.length, 0);
238
+ assert(!doc.getElementById('localized').classList.contains(hiddenClass));
239
+ assert(doc.getElementById('kept').classList.contains(hiddenClass));
240
+ assert(doc.getElementById('audits').classList.contains(hiddenClass));
241
+ observer.disconnect();
242
+ });
243
+
244
+ test('observer applies a mutation that arrives while a frame is scheduled', async () => {
245
+ createDOM('<main></main>');
246
+ const doc = document;
247
+ const observer = installDocumentationHiding(
248
+ doc,
249
+ 'Documentation',
250
+ '/prefix/links/manual'
251
+ );
252
+ let frame = null;
253
+ jest.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => {
254
+ frame = callback;
255
+ return 1;
256
+ });
257
+ const link = doc.createElement('a');
258
+ link.href =
259
+ 'https://docs.redhat.com/en/documentation/red_hat_satellite/6.19/guide';
260
+ doc.body.appendChild(link);
261
+ await Promise.resolve();
262
+ assert.equal(typeof frame, 'function');
263
+ const scheduledFrame = frame;
264
+ const button = doc.createElement('button');
265
+ button.setAttribute('data-ouia-component-id', 'audits-documentation-button');
266
+ doc.body.appendChild(button);
267
+ await Promise.resolve();
268
+ scheduledFrame();
269
+ assert.equal(link.getAttribute('href'), '/prefix/links/manual');
270
+ assert(button.classList.contains(hiddenClass));
271
+ observer.disconnect();
272
+ });
273
+
274
+ test('Reports inline sentence stays complete even beside a hidden documentation button', async () => {
275
+ createDOM(
276
+ '<ul><li id="report"><div class="empty-state-description">If you wish to configure Puppet to forward its reports to Foreman, please follow <a href="/links/manual/3.5.4PuppetReports">setting up reporting</a> and <a href="/links/wiki/Mail_Notifications">e-mail reporting</a></div><button data-ouia-component-id="audits-documentation-button">Documentation</button></li></ul>'
277
+ );
278
+ const doc = document;
279
+ hideDocumentation(doc);
280
+ normalizeSatelliteDocumentation(doc, '/links/manual');
281
+ assert.equal(
282
+ doc.querySelector('.empty-state-description').textContent,
283
+ 'If you wish to configure Puppet to forward its reports to Foreman, please follow setting up reporting and e-mail reporting'
284
+ );
285
+ doc.querySelectorAll('.empty-state-description a').forEach((link) => {
286
+ for (let node = link; node; node = node.parentElement) {
287
+ assert.notEqual(window.getComputedStyle(node).display, 'none');
288
+ }
289
+ });
290
+ assert.equal(
291
+ window.getComputedStyle(doc.querySelector('button')).display,
292
+ 'none'
293
+ );
294
+ });
data/webpack/index.js CHANGED
@@ -3,25 +3,56 @@ import React from 'react';
3
3
  import componentRegistry from 'foremanReact/components/componentRegistry';
4
4
  import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { foremanUrl } from 'foremanReact/common/helpers';
6
7
  import { Helmet } from 'react-helmet';
7
8
 
9
+ import { installDocumentationHiding } from './documentation/hideDocumentation';
10
+
8
11
  import SatelliteUpgradeHelperCard from './components/fills/UpgradePage/SatelliteUpgradeHelperCard';
12
+ import { SatelliteWelcomeEnv } from './components/SatelliteWelcomeEnv';
13
+
14
+ const SATELLITE_FILL_WEIGHT = 1000;
9
15
 
10
16
  // register components for erb mounting
11
17
  componentRegistry.register({ name: 'Helmet', type: Helmet });
18
+ componentRegistry.register({
19
+ name: 'SatelliteWelcomeEnv',
20
+ type: SatelliteWelcomeEnv,
21
+ });
12
22
 
13
23
  addGlobalFill(
14
24
  'upgrade-page-upgrade-docs',
15
25
  'Satellite upgrade helper card',
16
26
  <SatelliteUpgradeHelperCard key="satellite-upgrade-helper-card" />,
17
- 1000,
27
+ SATELLITE_FILL_WEIGHT
18
28
  );
19
29
  addGlobalFill(
20
30
  'upgrade-page-footer',
21
31
  'Satellite upgrade page footer',
22
32
  {
23
- helpDesc: __('If you require assistance or encounter issues, reach out to Red Hat Support or explore the resources available in the Customer Portal for troubleshooting.'),
33
+ helpDesc: __(
34
+ 'If you require assistance or encounter issues, reach out to Red Hat Support or explore the resources available in the Customer Portal for troubleshooting.'
35
+ ),
24
36
  helpLinkText: __('Contact support'),
25
37
  },
26
- 1000,
38
+ SATELLITE_FILL_WEIGHT
27
39
  );
40
+
41
+ if (document.readyState === 'loading') {
42
+ document.addEventListener(
43
+ 'DOMContentLoaded',
44
+ () =>
45
+ installDocumentationHiding(
46
+ document,
47
+ __('Documentation'),
48
+ foremanUrl('/links/manual')
49
+ ),
50
+ { once: true }
51
+ );
52
+ } else {
53
+ installDocumentationHiding(
54
+ document,
55
+ __('Documentation'),
56
+ foremanUrl('/links/manual')
57
+ );
58
+ }
@@ -0,0 +1,3 @@
1
+ // Foreman's Jest configuration resolves this alias to its shared test setup.
2
+ // eslint-disable-next-line import/no-unresolved
3
+ import 'foremanJSTestSetup';
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_theme_satellite
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.4.2
4
+ version: 16.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shimon Stein
@@ -66,6 +66,7 @@ files:
66
66
  - app/assets/javascripts/foreman_theme_satellite/theme_ng_page_title.js
67
67
  - app/assets/stylesheets/foreman_theme_satellite/application_colors.css
68
68
  - app/assets/stylesheets/foreman_theme_satellite/bastion.scss
69
+ - app/assets/stylesheets/foreman_theme_satellite/documentation.scss
69
70
  - app/assets/stylesheets/foreman_theme_satellite/external_logout.scss
70
71
  - app/assets/stylesheets/foreman_theme_satellite/foreman_theme_satellite.css
71
72
  - app/assets/stylesheets/foreman_theme_satellite/login.scss
@@ -76,6 +77,7 @@ files:
76
77
  - app/assets/stylesheets/foreman_theme_satellite/wizard.scss
77
78
  - app/controllers/concerns/documentation_controller_branding.rb
78
79
  - app/controllers/concerns/provisioning_templates_controller_branding.rb
80
+ - app/helpers/documentation_visibility_helper.rb
79
81
  - app/helpers/provisioning_templates_helper_branding.rb
80
82
  - app/helpers/theme_application_helper.rb
81
83
  - app/helpers/theme_css_class_helper.rb
@@ -94,6 +96,7 @@ files:
94
96
  - app/models/concerns/setting_branding.rb
95
97
  - app/models/concerns/upstream_only_settings.rb
96
98
  - app/overrides/bootdisk_help_page.rb
99
+ - app/overrides/hide_documentation.rb
97
100
  - app/overrides/layouts/base/eol_banner.html.erb.deface
98
101
  - app/overrides/layouts/base/theme_react_root.html.erb.deface
99
102
  - app/overrides/layouts/base/theme_resources.html.erb.deface
@@ -148,8 +151,14 @@ files:
148
151
  - locale/zh_CN/LC_MESSAGES/foreman_theme_satellite.mo
149
152
  - locale/zh_CN/foreman_theme_satellite.po
150
153
  - package.json
154
+ - webpack/components/SatelliteWelcomeEnv.js
151
155
  - webpack/components/fills/UpgradePage/SatelliteUpgradeHelperCard.js
156
+ - webpack/documentation/documentationMenuKeyboard.js
157
+ - webpack/documentation/documentationMenuKeyboard.test.js
158
+ - webpack/documentation/hideDocumentation.js
159
+ - webpack/documentation/hideDocumentation.test.js
152
160
  - webpack/index.js
161
+ - webpack/test_setup.js
153
162
  homepage: https://github.com/RedHatSatellite/foreman_theme_satellite
154
163
  licenses:
155
164
  - GPL-3.0