diff --git a/MANIFEST.in b/MANIFEST.in
index abbb0bc..0f9860b 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,6 @@
prune common
include LICENSE
+include sphinx_search/_static/js/rtd_sphinx_search.js
include sphinx_search/_static/js/rtd_sphinx_search.min.js
+include sphinx_search/_static/css/rtd_sphinx_search.css
include sphinx_search/_static/css/rtd_sphinx_search.min.css
diff --git a/docs/conf.py b/docs/conf.py
index 390185f..c3de4fb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -176,3 +176,9 @@
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
+
+# -- Setup for 'confval' used in docs/configuration.rst ----------------------
+
+def setup(app):
+ app.add_object_type('confval', 'confval',
+ 'pair: %s; configuration value')
diff --git a/docs/configuration.rst b/docs/configuration.rst
new file mode 100644
index 0000000..fbe9981
--- /dev/null
+++ b/docs/configuration.rst
@@ -0,0 +1,18 @@
+Configuration
+=============
+
+The following settings are available.
+You can customize these configuration options in your ``conf.py`` file:
+
+.. confval:: rtd_sphinx_search_file_type
+
+ Description: Type of files to be included in the html.
+
+ Possible values:
+
+ - ``minified``: Include the minified and uglified CSS and JS files.
+ - ``un-minified``: Include the original CSS and JS files.
+
+ Default: ``'minified'``
+
+ Type: ``string``
diff --git a/docs/custom-design.rst b/docs/custom-design.rst
index 19b2cf8..4c15d22 100644
--- a/docs/custom-design.rst
+++ b/docs/custom-design.rst
@@ -4,49 +4,12 @@ Custom Design
If you want to change the styles of the search UI,
you can do so by `adding your custom stylesheet`_ to your documentation.
-Basic structure of the HTML which is generated for the full-page search UI
+Basic structure of the HTML which is generated for the search UI
is given below for the reference:
.. code-block:: html
-
+ * @param {*} data
+ * @return {Boolean} 'true' if type is "string" and length is > 0
+ */
+const _is_string = str => {
+ if (typeof str === "string" && str.length > 0) {
+ return true;
+ } else {
+ return false;
+ }
+};
+
+/**
+ * Checks if data type is a non-empty array
+ * @param {*} data data whose type is to be checked
+ * @return {Boolean} returns true if data is non-empty array, else returns false
+ */
+const _is_array = arr => {
+ if (Array.isArray(arr) && arr.length > 0) {
+ return true;
+ } else {
+ return false;
+ }
+};
+
+/**
+ * Generate and return html structure
+ * for a page section result.
*
- * @param {Object} data response data from the search backend
- * @param {String} projectName name (slug) of the project
- * @return {Object} a
node with class "search__result__box" and with inner nodes
+ * @param {Object} sectionData object containing the result data
+ * @param {String} page_link link of the main page. It is used to construct the section link
*/
-const generateSuggestionsList = (data, projectName) => {
- let search_result_box = createDomNode("div", {
- class: "search__result__box"
+const get_section_html = (sectionData, page_link) => {
+ let section_template =
+ ' \
+
\
+ \
+ ';
+
+ let domain_link = `${page_link}#${domainData._source.anchor}`;
+ let domain_role_name = domainData._source.role_name;
+ let domain_type_display = domainData._source.type_display;
+ let domain_doc_display = domainData._source.doc_display;
+ let domain_display_name = domainData._source.display_name;
+ let domain_name = domainData._source.name;
+
+ // take values from highlighted fields (if present)
+ if (domainData.highlight !== undefined && domainData.highlight !== null) {
+ let highlight = domainData.highlight;
+
+ let name = getHighlightListData(highlight, "domains.name");
+ let display_name = getHighlightListData(
+ highlight,
+ "domains.display_name"
+ );
+ let type_display = getHighlightListData(
+ highlight,
+ "domains.type_display"
+ );
- let search_result_title = createDomNode("h2", {
- class: "search__result__title"
- });
- // use highlighted title (if present)
- if (data.results[i].highlight.title !== undefined) {
- search_result_title.innerHTML = data.results[i].highlight.title[0];
- } else {
- search_result_title.innerHTML = data.results[i].title;
+ if (name) {
+ domain_name = name[0];
}
- content.appendChild(search_result_title);
- content.appendChild(createDomNode("br"));
+ if (display_name) {
+ domain_display_name = display_name[0];
+ }
- let search_result_path = createDomNode("small", {
- class: "search__result__path"
- });
- search_result_path.innerHTML = data.results[i].path;
-
- // check if the corresponding result is from same project or not.
- // if it is not from same project, then it must be from a subproject.
- // display the subproject.
- if (data.results[i].project !== projectName) {
- search_result_path.innerHTML =
- data.results[i].path +
- " (from " +
- data.results[i].project +
- ")";
+ if (type_display) {
+ domain_type_display = type_display[0];
}
+ }
- content.appendChild(search_result_path);
+ // preparing domain_content
+ let domain_content = "";
+ if (_is_string(domain_type_display)) {
+ // domain_content = type_display --
+ domain_content += domain_type_display + " -- ";
+ }
+ if (_is_string(domain_name)) {
+ // domain_content = type_display -- name
+ domain_content += domain_name + " ";
+ }
+ if (_is_string(domain_doc_display)) {
+ // domain_content = type_display -- name -- in doc_display
+ domain_content += "-- in " + domain_doc_display;
+ }
- let search_result_content = createDomNode("p", {
- class: "search__result__content"
- });
- if (data.results[i].highlight.content !== undefined) {
- search_result_content.innerHTML =
- "... " + data.results[i].highlight.content + " ...";
- } else {
- search_result_content.innerHTML = "";
+ let domain_subheading = "";
+ if (_is_string(domain_display_name)) {
+ // domain_subheading = (role_name) display_name
+ domain_subheading = "(" + domain_role_name + ") " + domain_display_name;
+ } else {
+ // domain_subheading = role_name
+ domain_subheading = domain_role_name;
+ }
+
+ let domain_id = "hit__" + COUNT;
+ let domain_html = $u.template(domain_template, {
+ domain_link: domain_link,
+ domain_id: domain_id,
+ domain_content: domain_content,
+ domain_subheading: domain_subheading
+ });
+
+ return domain_html;
+};
+
+/**
+ * Generate search results for a single page.
+ *
+ * @param {Object} resultData search results of a page
+ * @return {Object} a
node with the results of a single page
+ */
+const generateSingleResult = (resultData, projectName) => {
+ let content = createDomNode("div");
+
+ let page_link_template =
+ ' \
+
'},showSearchModal=function(){removeResults(),getInputField().blur(),$(".search__outer__wrapper").fadeIn(400,function(){var e=document.querySelector(".search__outer__input");null!==e&&(e.value="",e.focus())})},removeSearchModal=function(){removeResults();var e=document.querySelector(".search__outer__input");null!==e&&(e.value="",e.blur()),$(".search__outer__wrapper").fadeOut(400)};window.addEventListener("DOMContentLoaded",function(e){if(void 0!==READTHEDOCS_DATA){var r=READTHEDOCS_DATA.project,i=READTHEDOCS_DATA.version,o=READTHEDOCS_DATA.language||"en",s=READTHEDOCS_DATA.api_host,t=generateAndReturnInitialHtml();document.body.innerHTML+=t;var n=document.querySelector(".search__outer__wrapper"),a=document.querySelector(".search__outer__input"),c=document.querySelector(".search__cross"),l=0,u=null,_=getInputField();_.addEventListener("focus",function(e){showSearchModal()}),a.addEventListener("input",function(e){SEARCH_QUERY=e.target.value,COUNT=0;var t={q:encodeURIComponent(SEARCH_QUERY),project:r,version:i,language:o},n=s+"/api/v2/docsearch/?"+convertObjToUrlParams(t);"string"==typeof SEARCH_QUERY&&0Sphinx",
- "Using Markdown with Sphinx"
- ],
- "title": ["Getting Started with Sphinx"],
- "content": [
- "Getting Started with Sphinx. Sphinx is a powerful documentation generator that has many great features",
- "docs. Run sphinx-quickstart in there:. cd docs sphinx-quickstart. This quick start will walk you through",
- "Using Markdown with Sphinx. You can use Markdown and reStructuredText in the same Sphinx project."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Adding Custom CSS or JavaScript to a Sphinx Project",
- "path": "guides/adding-custom-css",
- "link": "https://docs.readthedocs.io/en/latest/guides/adding-custom-css",
- "highlight": {
- "headers": [
- "Adding Custom CSS or JavaScript to a Sphinx Project"
- ],
"title": [
- "Adding Custom CSS or JavaScript to a Sphinx Project"
- ],
- "content": [
- "Adding Custom CSS or JavaScript to a Sphinx Project. The easiest way to add custom stylesheets or scripts",
- "way that allows for overriding of existing styles or scripts, is to add these files using a conf.py Sphinx",
- "Inside your conf.py, if a function setup(app) exists, Sphinx will call this function as a normal extension"
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Read the Docs data passed to Sphinx build context",
- "path": "design/theme-context",
- "link": "https://docs.readthedocs.io/en/latest/design/theme-context",
- "highlight": {
- "headers": [
- "Read the Docs data passed to Sphinx build context"
- ],
- "title": [
- "Read the Docs data passed to Sphinx build context"
- ],
- "content": [
- "Read the Docs data passed to Sphinx build context. Before calling sphinx-build to render your docs, Read",
- "the Docs injects some extra context in the templates by using the html_context Sphinx setting in the",
- "More information about Sphinx variables can be found in the Sphinx documentation."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Customizing Advertising",
- "path": "advertising/ad-customization",
- "link": "https://docs.readthedocs.io/en/latest/advertising/ad-customization",
- "highlight": {
- "headers": ["In Sphinx"],
- "content": [
- "<div id="ethical-ad-placement"></div>. In Sphinx. In Sphinx, this is typically done by adding a new template"
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Embed API",
- "path": "embed",
- "link": "https://docs.readthedocs.io/en/latest/embed",
- "highlight": {
- "headers": ["Sphinx Extension"],
- "content": [
- "How to use it. Sphinx Extension. You can embed content directly in Sphinx with builds on Read the Docs."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "User-defined Redirects",
- "path": "user-defined-redirects",
- "link": "https://docs.readthedocs.io/en/latest/user-defined-redirects",
- "highlight": {
- "headers": ["Sphinx Redirects"],
- "content": [
- "Sphinx Redirects. We also support redirects for changing the type of documentation Sphinx is building."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Build Process",
- "path": "builds",
- "link": "https://docs.readthedocs.io/en/latest/builds",
- "highlight": {
- "headers": ["Sphinx"],
- "content": [
- "Sphinx. When you choose Sphinx as your Documentation Type, we will first look for a conf.py file in your",
- "Then Sphinx will build any files with an .rst extension.",
- "When we build your Sphinx documentation, we run sphinx-build -b html ."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Configuration File V2",
- "path": "config-file/v2",
- "link": "https://docs.readthedocs.io/en/latest/config-file/v2",
- "highlight": {
- "headers": ["sphinx"],
- "content": [
- "sphinx: configuration: docs/conf.py # Build documentation with MkDocs #mkdocs: # configuration: mkdocs.yml",
- "Configuration for Sphinx documentation (this is the default documentation type).",
- "type for the Sphinx documentation."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Google Summer of Code",
- "path": "gsoc",
- "link": "https://docs.readthedocs.io/en/latest/gsoc",
- "highlight": {
- "headers": ["Build a new Sphinx theme"],
- "content": [
- "Integration with OpenAPI/Swagger. Integrate the existing tooling around OpenAPI & Swagger into Sphinx",
- "theme. Sphinx v2 will introduce a new format for themes, supporting HTML5 and new markup.",
- "We are hoping to buid a new Sphinx theme that supports this new structure."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Frequently Asked Questions",
- "path": "faq",
- "link": "https://docs.readthedocs.io/en/latest/faq",
- "highlight": {
- "headers": [
- "I want to use the Blue/Default Sphinx theme",
- "Sphinx"
- ],
- "content": [
- "With Sphinx you can use the built-in autodoc_mock_imports for mocking.",
- "Where it finds the conf.py, it will run sphinx-build in that directory.",
- "Sphinx. Sphinx uses html_extra option to add static files to the output."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Manage Translations",
- "path": "guides/manage-translations",
- "link": "https://docs.readthedocs.io/en/latest/guides/manage-translations",
- "highlight": {
- "content": [
- "Translate text from source language. Manually. We recommend using sphinx-intl tool for this workflow.",
- ""It supports :ref:`Sphinx <sphinx>` docs written with reStructuredText." msgstr "" "FILL HERE BY TARGET",
- "LANGUAGE FILL HERE BY TARGET LANGUAGE FILL HERE " "BY TARGET LANGUAGE :ref:`Sphinx <sphinx>` FILL HERE"
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Build PDF format for non-ASCII languages",
- "path": "guides/pdf-non-ascii-languages",
- "link": "https://docs.readthedocs.io/en/latest/guides/pdf-non-ascii-languages",
- "highlight": {
- "content": [
- "Build PDF format for non-ASCII languages. Sphinx offers different LaTeX engines that support Unicode characters",
- "By default Sphinx uses pdflatex, which does not have good support for Unicode characters and may make",
- "There are several settings that can be defined (all the ones starting with latex_), to modify Sphinx"
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Conda Support",
- "path": "conda",
- "link": "https://docs.readthedocs.io/en/latest/conda",
- "highlight": {
- "content": [
- "This Conda environment will also have Sphinx and other build time dependencies installed.",
- "operations that we support currently:. Environment Creation (conda env create). Dependency Installation (Sphinx"
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Local VM Install",
- "path": "custom_installs/local_rtd_vm",
- "link": "https://docs.readthedocs.io/en/latest/custom_installs/local_rtd_vm",
- "highlight": {
- "content": [
- "/docs/source. Sphinx:. sudo pip install sphinx. Note. Not using sudo may prevent access.",
- "Run the following Sphinx commands:. make html. This generates the HTML documentation site using the default",
- "Sphinx theme."
- ]
- }
- },
- {
- "project": "docs",
- "version": "latest",
- "title": "Read the Docs: Documentation Simplified",
- "path": "index",
- "link": "https://docs.readthedocs.io/en/latest/index",
- "highlight": {
- "content": [
- "Learn about documentation authoring tools such as Sphinx and MkDocs to help you create fantastic documentation",
- "I want to use the Blue/Default Sphinx theme. I want to use the Read the Docs theme locally. Image scaling",
- "Configuration File. Version 2. Version 1. Guides. Adding Custom CSS or JavaScript to a Sphinx Project. Enabling"
- ]
- }
+ "Developing a “Helloworld” extension"
+ ]
+ },
+ "inner_hits": [
+ {
+ "type": "sections",
+ "_source": {
+ "id": "overview",
+ "title": "Overview",
+ "content": "We want the extension to add the following to Sphinx:\nA helloworld directive, that will simply output the text “hello world”."
+ },
+ "highlight": {
+ "sections.content": ["world”."]
+ }
+ },
+ {
+ "type": "sections",
+ "_source": {
+ "id": "developing-a-hello-world-extension",
+ "title": "Developing a “Hello world” extension",
+ "content": "Only basic information is provided in this tutorial. For more information, refer to the other tutorials that go into more details.\n\nWarning\nFor this extension, you will need some basic understanding of docutils and Python.\n"
+ },
+ "highlight": {
+ "sections.title": [
+ "Developing a “Helloworld” extension"
+ ]
+ }
+ },
+ {
+ "type": "sections",
+ "_source": {
+ "id": "using-the-extension",
+ "title": "Using the extension",
+ "content": "The extension has to be declared in your conf.py file to make Sphinx aware of it. There are two steps necessary here:\nAdd the _ext directory to the Python path using sys.path.append. This should be placed at the top of the file.\nUpdate or create the extensions list and add the extension file name to the list\nFor example:\nimport os import sys sys.path.append(os.path.abspath(\"./_ext\")) extensions = ['helloworld']\nTip\nWe’re not distributing this extension as a Python package, we need to modify the Python path so Sphinx can find our extension. This is why we need the call to sys.path.append.\nYou can now use the extension in a file. For example:\nSome intro text here... .. helloworld:: Some more text here...\nThe sample above would generate:\nSome intro text here... Hello World! Some more text here..."
+ },
+ "highlight": {
+ "sections.content": [
+ "HelloWorld! Some more text here..."
+ ]
+ }
+ }
+ ]
}
]
}
diff --git a/tests/test_extension.py b/tests/test_extension.py
index fe47730..97cc903 100644
--- a/tests/test_extension.py
+++ b/tests/test_extension.py
@@ -4,7 +4,9 @@
import os
import pytest
+
from tests import TEST_DOCS_SRC
+from sphinx_search.extension import ASSETS_FILES
@pytest.mark.sphinx(srcdir=TEST_DOCS_SRC)
@@ -13,13 +15,62 @@ def test_static_files_exists(app, status, warning):
app.build()
path = app.outdir
- js_file = os.path.join(path, '_static', 'js', 'rtd_sphinx_search.min.js')
- css_file = os.path.join(path, '_static', 'css', 'rtd_sphinx_search.min.css')
+ static_files = ASSETS_FILES['minified'] + ASSETS_FILES['un-minified']
+
+ for file in static_files:
+ file_path = os.path.join(path, '_static', file)
+ assert (
+ os.path.exists(file_path)
+ ), f'{file_path} should be present in the _build folder'
+
+
+@pytest.mark.sphinx(
+ srcdir=TEST_DOCS_SRC,
+ confoverrides={
+ 'rtd_sphinx_search_file_type': 'minified'
+ }
+)
+def test_minified_static_files_injected_in_html(selenium, app, status, warning):
+ """Test if the static files are correctly injected in the html."""
+ app.build()
+ path = app.outdir / 'index.html'
+
+ selenium.get(f'file://{path}')
+ page_source = selenium.page_source
+
+ assert app.config.rtd_sphinx_search_file_type == 'minified'
+
+ file_type = app.config.rtd_sphinx_search_file_type
+ files = ASSETS_FILES[file_type]
+
+ for file in files:
+ file_name = file.split('/')[-1]
+ assert (
+ page_source.count(file_name) == 1
+ ), f'{file} should be present in the page source'
+
+
+@pytest.mark.sphinx(
+ srcdir=TEST_DOCS_SRC,
+ confoverrides={
+ 'rtd_sphinx_search_file_type': 'un-minified'
+ }
+)
+def test_un_minified_static_files_injected_in_html(selenium, app, status, warning):
+ """Test if the static files are correctly injected in the html."""
+ app.build()
+ path = app.outdir / 'index.html'
+
+ selenium.get(f'file://{path}')
+ page_source = selenium.page_source
+
+ assert app.config.rtd_sphinx_search_file_type == 'un-minified'
- assert (
- os.path.exists(js_file) is True
- ), 'js file should be copied to build folder'
+ file_type = app.config.rtd_sphinx_search_file_type
+ files = ASSETS_FILES[file_type]
- assert (
- os.path.exists(css_file) is True
- ), 'css file should be copied to build folder'
+ for file in files:
+ file_name = file.split('/')[-1]
+ assert (
+ page_source.count(file_name) == 1
+ ), f'{file_name} should be present in the page source'
diff --git a/tests/test_ui.py b/tests/test_ui.py
index 30c5339..6d7cc97 100644
--- a/tests/test_ui.py
+++ b/tests/test_ui.py
@@ -98,7 +98,7 @@ def test_appending_of_initial_html(selenium, app, status, warning):
'''
- # removing all whitespaces and newlines between html tags
+
initial_html = [ele.strip() for ele in initial_html.split('\n') if ele]
- assert (
- ''.join(initial_html) in selenium.page_source
- ), 'initial html must be present when the page finished loading.'
+ for line in initial_html:
+ if line:
+ assert (
+ line in selenium.page_source
+ ), f'{line} -- must be present in page source'
+
@pytest.mark.sphinx(srcdir=TEST_DOCS_SRC)
def test_opening_of_search_modal(selenium, app, status, warning):
@@ -376,9 +379,6 @@ def test_searching_msg(selenium, app, status, warning):
assert (
search_result_box.text == 'Searching ....'
), 'user should be notified that search is in progress'
- assert (
- len(search_result_box.find_elements_by_css_selector('*')) == 0
- ), 'search result box should not have any child elements because there are no results'
WebDriverWait(selenium, 10).until(
EC.text_to_be_present_in_element(
@@ -387,11 +387,14 @@ def test_searching_msg(selenium, app, status, warning):
)
)
- # fetching search_result_box again to update its content
+ # fetching it again from the DOM to update its status
search_result_box = selenium.find_element_by_class_name(
'search__result__box'
)
+ assert (
+ len(search_result_box.find_elements_by_css_selector('*')) == 0
+ ), 'search result box should not have any child elements because there are no results'
assert (
search_result_box.text == 'No Results Found'
), 'user should be notified that there are no results'
@@ -449,8 +452,16 @@ def test_results_displayed_to_user(selenium, app, status, warning):
'search__result__single'
)
)
- == 10
- ), 'search result box should have maximum 10 results'
+ == 1
+ ), 'search result box should have results from only 1 page (as per the dummy_results.json)'
+
+ assert (
+ len(
+ search_result_box.find_elements_by_class_name(
+ 'outer_div_page_results'
+ )
+ ) == 3
+ ), 'total 3 results should be shown to the user (as per the dummy_results.json)'
@pytest.mark.sphinx(srcdir=TEST_DOCS_SRC)
@@ -499,28 +510,28 @@ def test_navigate_results_with_arrow_up_and_down(selenium, app, status, warning)
'search__result__box'
)
results = selenium.find_elements_by_class_name(
- 'search__result__single'
+ 'outer_div_page_results'
)
search_outer_input.send_keys(Keys.DOWN)
assert results[0] == selenium.find_element_by_css_selector(
- '.search__result__single.active'
+ '.outer_div_page_results.active'
), 'first result should be active'
search_outer_input.send_keys(Keys.DOWN)
assert results[1] == selenium.find_element_by_css_selector(
- '.search__result__single.active'
+ '.outer_div_page_results.active'
), 'second result should be active'
search_outer_input.send_keys(Keys.UP)
search_outer_input.send_keys(Keys.UP)
assert results[-1] == selenium.find_element_by_css_selector(
- '.search__result__single.active'
+ '.outer_div_page_results.active'
), 'last result should be active'
search_outer_input.send_keys(Keys.DOWN)
assert results[0] == selenium.find_element_by_css_selector(
- '.search__result__single.active'
+ '.outer_div_page_results.active'
), 'first result should be active'
diff --git a/tox.ini b/tox.ini
index 868a1b8..1156392 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,7 @@
[tox]
minversion = 3.10
envlist =
- py{36}-sphinx{18}
+ py{36,37}-sphinx{18}
py{36,37}-sphinx{20,21}
docs
skipsdist = True
@@ -13,7 +13,7 @@ deps =
pytest-selenium
sphinx18: Sphinx<1.9
sphinx20: Sphinx<2.1
- sphinx21: Sphinx==2.1.0
+ sphinx21: Sphinx<2.2
commands = pytest {posargs}
[testenv:docs]