Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions assets/css/faq.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* DESCRIPTION: Frontend styles for the FAQ shortcode section
* and the [wedocs_faq] shortcode output. */

.wedocs-faq-section {
margin-top: 40px;
padding-top: 30px;
border-top: 1px solid #e5e7eb;
}

.wedocs-faq-section__title {
font-size: 24px;
font-weight: 600;
margin-bottom: 24px;
}

.wedocs-faq-group {
margin-bottom: 24px;
}

.wedocs-faq-group__title {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}

.wedocs-faq-group__icon {
width: 24px;
height: 24px;
object-fit: contain;
flex-shrink: 0;
}

.wedocs-faq-item {
border: 1px solid #e5e7eb;
border-radius: 6px;
margin-bottom: 8px;
overflow: hidden;
}

.wedocs-faq-item__question {
padding: 14px 18px;
cursor: pointer;
font-weight: 500;
font-size: 15px;
list-style: none;
display: flex;
align-items: center;
justify-content: space-between;
}

.wedocs-faq-item__question::-webkit-details-marker {
display: none;
}

.wedocs-faq-item__question::after {
content: '+';
font-size: 20px;
font-weight: 300;
color: #6b7280;
transition: transform 0.3s ease;
}

.wedocs-faq-item[open] .wedocs-faq-item__question::after {
content: '\2212';
}

.wedocs-faq-item__answer {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}

.wedocs-faq-item[open] .wedocs-faq-item__answer {
grid-template-rows: 1fr;
}

.wedocs-faq-item__answer-inner {
overflow: hidden;
padding: 0 18px;
font-size: 14px;
line-height: 1.6;
color: #4b5563;
}

.wedocs-faq-item[open] .wedocs-faq-item__answer-inner {
padding-bottom: 14px;
}
33 changes: 33 additions & 0 deletions assets/js/faq.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// DESCRIPTION: Smooth expand/collapse transitions for FAQ <details>
// elements rendered by the [wedocs_faq] shortcode.

document.addEventListener( 'DOMContentLoaded', function () {
document.querySelectorAll( '.wedocs-faq-item' ).forEach( function ( details ) {
var summary = details.querySelector( '.wedocs-faq-item__question' );
var answer = details.querySelector( '.wedocs-faq-item__answer' );
Comment on lines +6 to +7

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null checks for querySelector results.

If the expected DOM structure is missing, querySelector will return null and subsequent property accesses will throw TypeError. Add defensive checks to prevent runtime errors.

🛡️ Proposed fix with null checks
     document.querySelectorAll( '.wedocs-faq-item' ).forEach( function ( details ) {
         var summary = details.querySelector( '.wedocs-faq-item__question' );
         var answer  = details.querySelector( '.wedocs-faq-item__answer' );
+
+        if ( ! summary || ! answer ) {
+            return;
+        }

         // Items open by default need the inline style set so transitions work.
         if ( details.open ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var summary = details.querySelector( '.wedocs-faq-item__question' );
var answer = details.querySelector( '.wedocs-faq-item__answer' );
var summary = details.querySelector( '.wedocs-faq-item__question' );
var answer = details.querySelector( '.wedocs-faq-item__answer' );
if ( ! summary || ! answer ) {
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/js/faq.js` around lines 6 - 7, The code assigns summary and answer
from details.querySelector without checking for null, which can cause TypeError
if DOM nodes are missing; update the logic around the variables summary and
answer in assets/js/faq.js to guard their usage by verifying each is non-null
before accessing properties or attaching event listeners (e.g., skip processing
this details entry or bail out early when summary or answer is null), and ensure
any downstream code that assumes summary/answer exists is similarly protected.


// Items open by default need the inline style set so transitions work.
if ( details.open ) {
answer.style.gridTemplateRows = '1fr';
}

summary.addEventListener( 'click', function ( e ) {
e.preventDefault();

if ( details.open ) {
// Closing: let the transition finish before removing open.
answer.style.gridTemplateRows = '0fr';
answer.addEventListener( 'transitionend', function handler() {
details.open = false;
answer.removeEventListener( 'transitionend', handler );
} );
} else {
// Opening: set open first so content is measurable, then animate.
details.open = true;
// Force a reflow so the browser registers the 0fr starting state.
answer.offsetHeight; // eslint-disable-line no-unused-expressions
answer.style.gridTemplateRows = '1fr';
}
} );
} );
} );
46 changes: 42 additions & 4 deletions includes/Admin/Menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,46 @@ public function __construct() {
* @return void
*/
public function add_admin_menu() {
$parent_slug = 'wedocs';
add_menu_page(
__( 'weDocs', 'wedocs' ),
__( 'weDocs', 'wedocs' ),
$this->capability,
'wedocs',
$parent_slug,
array( $this, 'display_wedocs' ),
'dashicons-media-document',
$this->get_menu_position()
);

$faq = add_submenu_page( $parent_slug, __( 'FAQ', 'wedocs' ), __( 'FAQ', 'wedocs' ), $this->capability, 'wedocs-faq', array( $this, 'display_faq' ) );

add_action( 'load-' . $faq, [ $this, 'faq_menu_action' ] );
}

/**
* Fire the FAQ page load hook.
*
* @since WEDOCS_SINCE
*
* @return void
*/
public function faq_menu_action() {
/**
* Backdoor for calling the menu hook.
* This hook won't get translated even the site language is changed
*/
do_action( 'wedocs_load_faq_page' );
}

/**
* Display FAQ page.
*
* @since WEDOCS_SINCE
*
* @return void
*/
public function display_faq() {
wedocs_get_template_part( 'admin/faq' );
}

/**
Expand Down Expand Up @@ -78,6 +109,12 @@ public function add_admin_submenu() {
$this->capability,
'edit-tags.php?taxonomy=doc_tag&post_type=docs',
),
array(
__( 'FAQ', 'wedocs' ),
$this->capability,
'wedocs-faq',
__( 'FAQ', 'wedocs' ),
),
array(
__( 'Settings', 'wedocs' ),
apply_filters( 'wedocs_settings_management_capabilities', $this->capability ),
Expand All @@ -91,9 +128,10 @@ public function add_admin_submenu() {
);

$all_submenus = apply_filters( 'wedocs_submenu', $all_submenus );
if ( empty( $submenu['wedocs'] ) ) {
$submenu['wedocs'] = array(); // phpcs:ignore.
}

// Reset submenu to remove the auto-generated parent duplicate
// that WordPress creates when add_submenu_page() is used.
$submenu['wedocs'] = array(); // phpcs:ignore.

array_push(
$submenu['wedocs'],
Expand Down
44 changes: 44 additions & 0 deletions includes/Assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public function __construct() {
add_action( 'init', array( $this, 'register' ) );
add_action( 'init', array( $this, 'register_translations' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
add_action( 'wedocs_load_faq_page', array( $this, 'enqueue_faq_assets' ) );
}

/**
Expand Down Expand Up @@ -131,6 +132,36 @@ public function register() {
);
}

// Register FAQ assets.
if ( file_exists( WEDOCS_PATH . '/assets/build/faq.asset.php' ) ) {
$faq_dependencies = require WEDOCS_PATH . '/assets/build/faq.asset.php';

wp_register_style(
'wedocs-faq-style',
$assets_url . '/build/faq.css',
array(),
$faq_dependencies['version'],
);

wp_register_script(
'wedocs-faq-script',
$assets_url . '/build/faq.js',
$faq_dependencies['dependencies'],
$faq_dependencies['version'],
true
);

wp_localize_script(
'wedocs-faq-script',
'weDocsFaqVars',
array(
'adminUrl' => admin_url(),
'restNonce' => wp_create_nonce( 'wp_rest' ),
'hasManageCap' => current_user_can( 'manage_options' ),
),
);
}

wp_enqueue_style( 'wedocs-block-style' );
}

Expand Down Expand Up @@ -175,4 +206,17 @@ public function admin_enqueue() {
wp_enqueue_script( 'wedocs-editor-script' );
}
}

/**
* Enqueue FAQ page assets.
*
* @since WEDOCS_SINCE
*
* @return void
*/
public function enqueue_faq_assets() {
wp_enqueue_media();
wp_enqueue_style( 'wedocs-faq-style' );
wp_enqueue_script( 'wedocs-faq-script' );
}
}
2 changes: 2 additions & 0 deletions includes/Frontend.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public function register_scripts() {
// All scripts goes here
wp_register_script( 'wedocs-anchorjs', WEDOCS_ASSETS . '/js/anchor.min.js', [ 'jquery' ], WEDOCS_VERSION, true );
wp_register_script( 'wedocs-scripts', WEDOCS_ASSETS . '/js/frontend.js', [ 'jquery', 'wedocs-anchorjs' ], filemtime( WEDOCS_PATH . '/assets/js/frontend.js' ), true );
wp_register_style( 'wedocs-faq', WEDOCS_ASSETS . '/css/faq.css', [], filemtime( WEDOCS_PATH . '/assets/css/faq.css' ) );
wp_register_script( 'wedocs-faq', WEDOCS_ASSETS . '/js/faq.js', [], filemtime( WEDOCS_PATH . '/assets/js/faq.js' ), true );
Comment on lines +76 to +77

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for filemtime() to prevent warnings if files are missing.

If the CSS or JS files don't exist, filemtime() returns false, which will be cast to an empty string or 0 for the version parameter. This could cause caching issues or PHP warnings in production.

🛡️ Proposed fix with fallback versioning
-        wp_register_style( 'wedocs-faq', WEDOCS_ASSETS . '/css/faq.css', [], filemtime( WEDOCS_PATH . '/assets/css/faq.css' ) );
-        wp_register_script( 'wedocs-faq', WEDOCS_ASSETS . '/js/faq.js', [], filemtime( WEDOCS_PATH . '/assets/js/faq.js' ), true );
+        wp_register_style( 'wedocs-faq', WEDOCS_ASSETS . '/css/faq.css', [], filemtime( WEDOCS_PATH . '/assets/css/faq.css' ) ?: WEDOCS_VERSION );
+        wp_register_script( 'wedocs-faq', WEDOCS_ASSETS . '/js/faq.js', [], filemtime( WEDOCS_PATH . '/assets/js/faq.js' ) ?: WEDOCS_VERSION, true );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
wp_register_style( 'wedocs-faq', WEDOCS_ASSETS . '/css/faq.css', [], filemtime( WEDOCS_PATH . '/assets/css/faq.css' ) );
wp_register_script( 'wedocs-faq', WEDOCS_ASSETS . '/js/faq.js', [], filemtime( WEDOCS_PATH . '/assets/js/faq.js' ), true );
wp_register_style( 'wedocs-faq', WEDOCS_ASSETS . '/css/faq.css', [], filemtime( WEDOCS_PATH . '/assets/css/faq.css' ) ?: WEDOCS_VERSION );
wp_register_script( 'wedocs-faq', WEDOCS_ASSETS . '/js/faq.js', [], filemtime( WEDOCS_PATH . '/assets/js/faq.js' ) ?: WEDOCS_VERSION, true );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/Frontend.php` around lines 70 - 71, The wp_register_style and
wp_register_script calls use filemtime(...) directly which can emit warnings or
return false if the files are missing; update the version argument for both
'wedocs-faq' registrations to first check existence of WEDOCS_PATH .
'/assets/css/faq.css' and WEDOCS_PATH . '/assets/js/faq.js' respectively (via
file_exists or is_readable) and use the filemtime value when present, otherwise
fall back to a safe fallback (e.g. null or a stable fallback like time() or a
constant) to avoid warnings and incorrect versioning.


$store_dependencies = require WEDOCS_PATH . '/assets/build/store.asset.php';
wp_register_script( 'wedocs-store-js', WEDOCS_ASSETS . '/build/store.js', $store_dependencies['dependencies'], $store_dependencies['version'], true );
Expand Down
Loading
Loading