Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dc092e3
Add Component based rendering with priority
abhishekxix Apr 29, 2026
fd4b542
Add output buffered get static method
abhishekxix Apr 29, 2026
c60aa58
Add convenience wrapper for get method
abhishekxix Apr 29, 2026
4512e3a
Fix global variable prefix phpcs error
abhishekxix Apr 30, 2026
223b9ab
Fix WP global phpcs errors
abhishekxix Apr 30, 2026
961f624
Fix short ternary error
abhishekxix Apr 30, 2026
18b7098
Address feedback
abhishekxix May 7, 2026
8641cd9
Merge branch 'theme-elementary-v2' into refactor/component-based-php-…
Adi-ty May 14, 2026
9b3ae81
feat: add enqueue assets filter and action that can be reused.
bhavz-10 May 17, 2026
a4feb94
feat: add assets enqeue by default for the components
bhavz-10 May 18, 2026
beb73b1
feat: optimize, add cache loader for storing the paths statically
bhavz-10 May 19, 2026
95e33c4
feat: add theme and plugin component functions for assets and php par…
bhavz-10 May 19, 2026
28da679
feat: add js, scss and configure webpack
bhavz-10 May 19, 2026
333b541
feat: add card css
bhavz-10 May 19, 2026
fb50d2c
Update component source path casing
bhavz-10 May 20, 2026
e3384fd
Lowercase component source files
bhavz-10 May 20, 2026
5f47b4c
Refactor ComponentLoader: optimize assets, add type safety, and clean…
bhavz-10 May 25, 2026
c294c98
Merge branch 'theme-elementary-v2' of github.com:rtCamp/theme-element…
bhavz-10 May 26, 2026
2931ff5
refactor: remove ComponentLoader and its test
bhavz-10 May 26, 2026
861f4ae
fix: update webpack config js
bhavz-10 May 26, 2026
154dbbf
tests: update webpack tests
bhavz-10 May 26, 2026
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
50 changes: 50 additions & 0 deletions inc/helpers/custom-functions.php
Comment thread
aryanjasala marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* Define custom functions for the theme.
*
* @package rtCamp\Theme\Elementary
*/

declare( strict_types = 1 );

use rtCamp\WPFramework\Components\ComponentLoader;

if ( ! function_exists( 'elementary_theme_component' ) ) {

/**
* Render a component by name.
*
* Global convenience wrapper for ComponentLoader::render().
*
* @since 1.0.0
*
* @param string $name Component name (e.g. 'Button', 'Card').
* @param array<string, mixed> $args Arguments to pass to the component.
* @param array<string, mixed> $options Optional. Resolution options. See ComponentLoader::render().
*
* @return void
*/
function elementary_theme_component( string $name, array $args = [], array $options = [] ): void {
ComponentLoader::render( $name, $args, $options );
}
}

if ( ! function_exists( 'elementary_theme_get_component' ) ) {

/**
* Get the rendered HTML of a component as a string.
*
* Global convenience wrapper for ComponentLoader::get().
*
* @since 1.0.0
*
* @param string $name Component name (e.g. 'Button', 'Card').
* @param array<string, mixed> $args Arguments to pass to the component.
* @param array<string, mixed> $options Optional. Resolution options. See ComponentLoader::get().
*
* @return string Rendered component HTML.
*/
function elementary_theme_get_component( string $name, array $args = [], array $options = [] ): string {
return ComponentLoader::get( $name, $args, $options );
}
}
4 changes: 4 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
</property>
</properties>
<exclude-pattern>tests/bootstrap.php</exclude-pattern>
<!-- Component partials are always require'd inside ComponentLoader::render(), so variables are method-scoped at runtime. -->
<exclude-pattern>src/components/*</exclude-pattern>
</rule>

<rule ref="WordPress-Docs">
Expand All @@ -112,6 +114,8 @@

<rule ref="WordPress.WP.GlobalVariablesOverride.Prohibited">
<exclude-pattern>tests/*</exclude-pattern>
<!-- Component partials are always require'd inside ComponentLoader::render(), so variables are method-scoped at runtime. -->
<exclude-pattern>src/components/*</exclude-pattern>
</rule>

<!--
Expand Down
9 changes: 9 additions & 0 deletions src/components/button/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Button component script.
*/
document.addEventListener('DOMContentLoaded', () => {

Check failure on line 4 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space after this paren
const buttons = document.querySelectorAll('.elementary-button');

Check failure on line 5 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space before this paren

Check failure on line 5 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space after this paren
if (buttons.length > 0) {

Check failure on line 6 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space before this paren

Check failure on line 6 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space after this paren
console.log(`Elementary Button component loaded. Found ${buttons.length} buttons.`);

Check failure on line 7 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space before this paren

Check failure on line 7 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

Expected space(s) before '}'

Check failure on line 7 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

Expected space(s) after '${'

Check failure on line 7 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

There must be a space after this paren

Check failure on line 7 in src/components/button/button.js

View workflow job for this annotation

GitHub Actions / Lint JS

Unexpected console statement
}
});
45 changes: 45 additions & 0 deletions src/components/button/button.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

declare( strict_types = 1 );

/**
* Button component.
*
* A render-only component that outputs a button or link element.
*
* @package rtCamp\Theme\Elementary
*
* @param array $args {
* Component arguments.
*
* @type string $label Button label text. Required.
* @type string $url URL for link buttons. Optional.
* @type string $class Additional CSS classes. Optional.
* @type string $tag HTML tag: 'a' or 'button'. Optional. Defaults to 'a' when $url is set, 'button' otherwise.
* }
*/

declare( strict_types = 1 );

$label = $args['label'] ?? '';
$url = $args['url'] ?? '';
$class = $args['class'] ?? '';
$tag = $args['tag'] ?? ( ! empty( $url ) ? 'a' : 'button' );

if ( empty( $label ) ) {
return;
}

$css_class = trim( 'elementary-button ' . $class );

if ( 'a' === $tag && ! empty( $url ) ) {
printf(
'<a href="%s" class="%s">%s</a>',
esc_url( $url ),
esc_attr( $css_class ),
esc_html( $label )
);
} else {
printf(
'<button type="button" class="%s">%s</button>',
esc_attr( $css_class ),
esc_html( $label )
);
}
23 changes: 23 additions & 0 deletions src/components/button/button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.elementary-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
line-height: 1.5;
color: #fff;
background-color: #0073aa;
border: 1px solid transparent;
border-radius: 4px;
text-decoration: none;
cursor: pointer;
transition: background-color 0.2s ease-in-out;

&:hover,
&:focus {
background-color: #005177;
text-decoration: none;
color: #fff;
}
}
9 changes: 9 additions & 0 deletions src/components/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Card component script.
*/
document.addEventListener('DOMContentLoaded', () => {
const cards = document.querySelectorAll('.elementary-card');
if (cards.length > 0) {
console.log(`Elementary Card component loaded. Found ${cards.length} cards.`);
}
});
70 changes: 70 additions & 0 deletions src/components/card/card.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/**
* Card component.
*
* A render-only component that outputs a card with optional image, title,
* description, and action button. Demonstrates component composability
* by rendering the Button component internally.
*
* @package rtCamp\Theme\Elementary
*
* @param array $args {
* Component arguments.
*
* @type string $title Card title. Required.
* @type string $description Card description text. Optional.
* @type string $image_url Card image URL. Optional.
* @type string $url Card link URL. Optional.
* }
*/

declare( strict_types = 1 );

$title = $args['title'] ?? '';
$description = $args['description'] ?? '';
$image_url = $args['image_url'] ?? '';
$url = $args['url'] ?? '';

if ( empty( $title ) ) {
return;
}

?>
<div class="elementary-card">
<?php if ( ! empty( $image_url ) ) : ?>
<div class="elementary-card__image">
<img src="<?php echo esc_url( $image_url ); ?>" alt="<?php echo esc_attr( $title ); ?>" />
</div>
<?php endif; ?>

<div class="elementary-card__content">
<h3 class="elementary-card__title"><?php echo esc_html( $title ); ?></h3>

<?php if ( ! empty( $description ) ) : ?>
<p class="elementary-card__description"><?php echo esc_html( $description ); ?></p>
<?php endif; ?>

<?php if ( ! empty( $url ) ) : ?>
<div class="elementary-card__action">
<?php
elementary_theme_component(
'Button',
[
'label' => $title,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nested component does not forward $options

Card renders Button with no $options, so Button always uses default enqueue settings regardless of what was passed to Card. A caller doing ComponentLoader::render('Card', $args, ['script' => false]) expects no scripts — but Button's script is enqueued anyway.

Forward relevant options:

ComponentLoader::render( 'Button', $button_args, array_intersect_key( $options, array_flip( [ 'script', 'style' ] ) ) );

Note: test_nested_components_inherit_disabled_enqueue_options passes today NOT because options are forwarded, but because assets/build/ is gitignored and doesn't exist in CI, so get_component_assets() returns [] regardless of options. The test gives false confidence — see also comment on that test.

'url' => $url,
'class' => 'elementary-card__button',
],
array_intersect_key(
$options,
[
'script' => true,
'style' => true,
]
)
);
?>
</div>
<?php endif; ?>
</div>
</div>
<?php
53 changes: 53 additions & 0 deletions src/components/card/card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.elementary-card {
display: flex;
flex-direction: column;
background-color: #fff;
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;

&:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}

&__image {
width: 100%;
height: 200px;
overflow: hidden;

img {
width: 100%;
height: 100%;
object-fit: cover;
}
}

&__content {
padding: 1.5rem;
display: flex;
flex-direction: column;
flex-grow: 1;
}

&__title {
font-size: 1.25rem;
font-weight: 600;
margin: 0 0 0.75rem;
color: #1a202c;
}

&__description {
font-size: 1rem;
color: #4a5568;
margin: 0 0 1.5rem;
line-height: 1.5;
flex-grow: 1;
}

&__action {
margin-top: auto;
}
}
3 changes: 0 additions & 3 deletions tests/js/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ module.exports = {
transform: {
'^.+\\.[jt]sx?$': '<rootDir>/node_modules/@wordpress/scripts/config/babel-transform',
},
setupFiles: [
'<rootDir>/tests/js/setup-globals',
],
preset: '@wordpress/jest-preset-default',
testPathIgnorePatterns: [
'<rootDir>/.git',
Expand Down
45 changes: 45 additions & 0 deletions tests/js/webpack-config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* External dependencies
*/
const fs = require( 'fs' );
const os = require( 'os' );
const path = require( 'path' );

jest.mock( '@wordpress/scripts/config/webpack.config', () => [
{
optimization: {
minimizer: [],
splitChunks: {},
},
plugins: [],
},
{},
] );

const { getComponentEntries } = require( '../../webpack.config' );

describe( 'webpack component entries', () => {
let tmpDir;

afterEach( () => {
if ( tmpDir ) {
fs.rmSync( tmpDir, { recursive: true, force: true } );
tmpDir = undefined;
}
} );

it( 'only matches files with the exact component basename', () => {
tmpDir = fs.mkdtempSync( path.join( os.tmpdir(), 'elementary-webpack-components-' ) );
const buttonDir = path.join( tmpDir, 'button' );

fs.mkdirSync( buttonDir );
fs.writeFileSync( path.join( buttonDir, 'button.js' ), '' );
fs.writeFileSync( path.join( buttonDir, 'button-extra.js' ), '' );
fs.writeFileSync( path.join( buttonDir, 'button.test.js' ), '' );
fs.writeFileSync( path.join( buttonDir, 'button_utils.js' ), '' );

expect( getComponentEntries( tmpDir, /\.js$/ ) ).toEqual( {
'components/button': path.join( buttonDir, 'button.js' ),
} );
} );
} );
Loading
Loading