📝 docs: Add implementation plan for team grid block
This commit is contained in:
@@ -0,0 +1,614 @@
|
|||||||
|
# Team Grid Block Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Build a new `team-grid` ACF block for the Team page (and any other page that needs a team grid) that renders a responsive grid of team members. Each member shows their photo (rounded rectangle), name, and title. The bio is collapsed by default and expands vertically with a smooth animation when the photo, name, or title is clicked.
|
||||||
|
|
||||||
|
**Architecture:** One new ACF block (`acf/team-grid`) with a single repeater of `members` (image, name, title, bio). The block uses native HTML `<details>`/`<summary>` for the click-to-expand interaction, animated with the `grid-template-rows: 0fr → 1fr` CSS pattern (no JavaScript). The block has no built-in background — the editor wraps it in an existing Section block when a dark/light background is needed. The page template is unchanged; the editor adds the block to the Team page in Gutenberg.
|
||||||
|
|
||||||
|
**Tech Stack:** WordPress 6.x, PHP 8.x, Tailwind CSS v4, Playwright, axe-core, PHPCS (WordPress standard).
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Tabs for PHP indentation (project standard).
|
||||||
|
- `static/dist/theme.css` is committed (not gitignored) per project convention.
|
||||||
|
- PHPCS uses the WordPress coding standard; `composer lint` must pass for all touched files.
|
||||||
|
- All design colors use the project's existing CSS custom properties from `styles/base/colors.css`. No hardcoded hex values for theme colors.
|
||||||
|
- Reuse existing design tokens where possible (e.g. `text-cwc-blue-01`, `var(--color-secondary)`).
|
||||||
|
- The headshot uses `rounded-md` to match other image cards in the project.
|
||||||
|
- All commits follow the project's emoji-prefixed commit-message style (e.g. `✨ feat: …`).
|
||||||
|
- This work does **not** edit `header.php`, `footer.php`, `views/partials/page-hero.php`, or `views/partials/page-hero-services.php`.
|
||||||
|
- `page.php` is not modified.
|
||||||
|
- No JavaScript files are added.
|
||||||
|
- Reuse the same `regACFBlocks()` auto-registration pattern as `services-list`.
|
||||||
|
- Tailwind v4 picks up class names from source files at build time. The dynamic class names used in the CSS (`grid-cols-1`, `sm:grid-cols-2`, `lg:grid-cols-3`, `w-32 h-32`, etc.) must all be literal in the source for the build to emit them.
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
| File | Responsibility | Created/Modified |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `views/blocks/team-grid/team-grid.php` | Block template (PHP) — renders the grid. | Create |
|
||||||
|
| `views/blocks/team-grid/team-grid.css` | Block styles (CSS) — layout, photo, summary, animation. | Create |
|
||||||
|
| `views/blocks/team-grid/block.json` | Block registration metadata. | Create |
|
||||||
|
| `acf/group_team_grid.json` | ACF field group for the `acf/team-grid` block. | Create |
|
||||||
|
| `styles/blocks/index.css` | Import the new block's CSS. | Modify |
|
||||||
|
| `tests/team-page.spec.js` | Playwright tests for the rendered block. | Create |
|
||||||
|
| `static/dist/theme.css` | Rebuilt via `npm run build`. | Modified |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: ACF Field Group
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `acf/group_team_grid.json`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: ACF group with `key: "group_team_grid"`, location scoped to `block == acf/team-grid`, fields: `members` (repeater) → `image`, `name`, `title`, `bio`.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the ACF field group JSON file**
|
||||||
|
|
||||||
|
Create the file `acf/group_team_grid.json` with the following exact content:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"key": "group_team_grid",
|
||||||
|
"title": "Team Grid",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "field_team_grid_members",
|
||||||
|
"label": "Members",
|
||||||
|
"name": "members",
|
||||||
|
"aria-label": "",
|
||||||
|
"type": "repeater",
|
||||||
|
"instructions": "Add team members. Click a card to expand the bio.",
|
||||||
|
"required": 0,
|
||||||
|
"conditional_logic": 0,
|
||||||
|
"wrapper": {
|
||||||
|
"width": "",
|
||||||
|
"class": "",
|
||||||
|
"id": ""
|
||||||
|
},
|
||||||
|
"layout": "block",
|
||||||
|
"pagination": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0,
|
||||||
|
"collapsed": "",
|
||||||
|
"button_label": "Add Team Member",
|
||||||
|
"rows_per_page": 20,
|
||||||
|
"sub_fields": [
|
||||||
|
{
|
||||||
|
"key": "field_team_grid_member_image",
|
||||||
|
"label": "Image",
|
||||||
|
"name": "image",
|
||||||
|
"aria-label": "",
|
||||||
|
"type": "image",
|
||||||
|
"instructions": "",
|
||||||
|
"required": 0,
|
||||||
|
"conditional_logic": 0,
|
||||||
|
"wrapper": {
|
||||||
|
"width": "",
|
||||||
|
"class": "",
|
||||||
|
"id": ""
|
||||||
|
},
|
||||||
|
"return_format": "array",
|
||||||
|
"library": "all",
|
||||||
|
"min_width": "",
|
||||||
|
"min_height": "",
|
||||||
|
"min_size": "",
|
||||||
|
"max_width": "",
|
||||||
|
"max_height": "",
|
||||||
|
"max_size": "",
|
||||||
|
"mime_types": "",
|
||||||
|
"allow_in_bindings": 0,
|
||||||
|
"preview_size": "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "field_team_grid_member_name",
|
||||||
|
"label": "Name",
|
||||||
|
"name": "name",
|
||||||
|
"aria-label": "",
|
||||||
|
"type": "text",
|
||||||
|
"instructions": "",
|
||||||
|
"required": 0,
|
||||||
|
"conditional_logic": 0,
|
||||||
|
"wrapper": {
|
||||||
|
"width": "",
|
||||||
|
"class": "",
|
||||||
|
"id": ""
|
||||||
|
},
|
||||||
|
"default_value": "",
|
||||||
|
"maxlength": "",
|
||||||
|
"allow_in_bindings": 0,
|
||||||
|
"placeholder": "",
|
||||||
|
"prepend": "",
|
||||||
|
"append": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "field_team_grid_member_title",
|
||||||
|
"label": "Title",
|
||||||
|
"name": "title",
|
||||||
|
"aria-label": "",
|
||||||
|
"type": "text",
|
||||||
|
"instructions": "",
|
||||||
|
"required": 0,
|
||||||
|
"conditional_logic": 0,
|
||||||
|
"wrapper": {
|
||||||
|
"width": "",
|
||||||
|
"class": "",
|
||||||
|
"id": ""
|
||||||
|
},
|
||||||
|
"default_value": "",
|
||||||
|
"maxlength": "",
|
||||||
|
"allow_in_bindings": 0,
|
||||||
|
"placeholder": "",
|
||||||
|
"prepend": "",
|
||||||
|
"append": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "field_team_grid_member_bio",
|
||||||
|
"label": "Bio",
|
||||||
|
"name": "bio",
|
||||||
|
"aria-label": "",
|
||||||
|
"type": "wysiwyg",
|
||||||
|
"instructions": "",
|
||||||
|
"required": 0,
|
||||||
|
"conditional_logic": 0,
|
||||||
|
"wrapper": {
|
||||||
|
"width": "",
|
||||||
|
"class": "",
|
||||||
|
"id": ""
|
||||||
|
},
|
||||||
|
"default_value": "",
|
||||||
|
"tabs": "visual",
|
||||||
|
"toolbar": "basic",
|
||||||
|
"media_upload": 0,
|
||||||
|
"delay": 0,
|
||||||
|
"allow_in_bindings": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"location": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"param": "block",
|
||||||
|
"operator": "==",
|
||||||
|
"value": "acf/team-grid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"menu_order": 0,
|
||||||
|
"position": "normal",
|
||||||
|
"style": "default",
|
||||||
|
"label_placement": "top",
|
||||||
|
"instruction_placement": "label",
|
||||||
|
"hide_on_screen": "",
|
||||||
|
"active": true,
|
||||||
|
"description": "Team grid block. Each member is a card with photo, name, title, and a collapsible bio.",
|
||||||
|
"show_in_rest": 0,
|
||||||
|
"display_title": "",
|
||||||
|
"allow_ai_access": false,
|
||||||
|
"ai_description": "",
|
||||||
|
"modified": 1753401600
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the JSON parses**
|
||||||
|
|
||||||
|
Run: `node -e "JSON.parse(require('fs').readFileSync('acf/group_team_grid.json','utf8')); console.log('OK')"`
|
||||||
|
Expected: prints `OK` and exits 0.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add acf/group_team_grid.json
|
||||||
|
git commit -m "✨ feat: Add ACF field group for team-grid block"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: Block Registration JSON
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `views/blocks/team-grid/block.json`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `block.json` declaring `acf/team-grid` as the block name, `team-grid.php` as the render template, `groups` icon, `common` category. `regACFBlocks()` in `functions.php` will pick this up automatically.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the block.json file**
|
||||||
|
|
||||||
|
Create the file `views/blocks/team-grid/block.json` with the following exact content:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "acf/team-grid",
|
||||||
|
"title": "Team Grid",
|
||||||
|
"description": "A responsive grid of team members. Click a card to expand the bio.",
|
||||||
|
"category": "common",
|
||||||
|
"icon": "groups",
|
||||||
|
"keywords": ["team", "people", "staff", "bio"],
|
||||||
|
"acf": {
|
||||||
|
"mode": "preview",
|
||||||
|
"renderTemplate": "team-grid.php"
|
||||||
|
},
|
||||||
|
"supports": {
|
||||||
|
"anchor": true,
|
||||||
|
"align": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the JSON parses**
|
||||||
|
|
||||||
|
Run: `node -e "JSON.parse(require('fs').readFileSync('views/blocks/team-grid/block.json','utf8')); console.log('OK')"`
|
||||||
|
Expected: prints `OK` and exits 0.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add views/blocks/team-grid/block.json
|
||||||
|
git commit -m "✨ feat: Add team-grid block.json registration"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Block CSS
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `views/blocks/team-grid/team-grid.css`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: BEM-styled CSS for `.team-grid`, `__inner`, `__card`, `__details`, `__summary`, `__photo`, `__name`, `__title`, `__bio-wrap`, `__bio-inner`, `__bio`. Reuses `text-cwc-blue-01` for the name. Implements the `grid-template-rows: 0fr → 1fr` animation for the bio.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the block CSS file**
|
||||||
|
|
||||||
|
Create the file `views/blocks/team-grid/team-grid.css` with the following exact content:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Team Grid block */
|
||||||
|
|
||||||
|
.team-grid {
|
||||||
|
&__card {
|
||||||
|
@apply flex flex-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
@apply flex flex-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__summary {
|
||||||
|
@apply flex flex-col items-center text-center cursor-pointer list-none;
|
||||||
|
|
||||||
|
&::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
display: none;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__photo {
|
||||||
|
@apply w-32 h-32 sm:w-40 sm:h-40 lg:w-48 lg:h-48 object-cover rounded-md mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
@apply text-cwc-blue-01 text-22px font-bold leading-tight mb-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
@apply text-gray-600 text-16px font-light italic mb-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bio-wrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 0fr;
|
||||||
|
transition: grid-template-rows 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__details[open] &__bio-wrap {
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bio-inner {
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bio {
|
||||||
|
@apply text-gray-700 text-16px font-light leading-snug pt-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the BEM children are present**
|
||||||
|
|
||||||
|
Run: `grep -cE "&__(card|details|summary|photo|name|title|bio-wrap|bio-inner|bio)\b" views/blocks/team-grid/team-grid.css`
|
||||||
|
Expected: `9` (one match per BEM child).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add views/blocks/team-grid/team-grid.css
|
||||||
|
git commit -m "✨ feat: Add team-grid block CSS"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Block Template (PHP)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `views/blocks/team-grid/team-grid.php`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `get_field( 'members' )` → array of members each with `image` (array: `url`, `alt`), `name` (string), `title` (string), `bio` (wysiwyg HTML).
|
||||||
|
- Produces: A `<section class="team-grid ...">` element with a `__inner` grid div, and an `<article class="team-grid__card">` per member. Each card contains a `<details>` with a `<summary>` (photo + name + title) and a `__bio-wrap` containing the bio.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the block PHP template**
|
||||||
|
|
||||||
|
Create the file `views/blocks/team-grid/team-grid.php` with the following exact content (tabs for indentation):
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Block Name: Team Grid
|
||||||
|
*
|
||||||
|
* A responsive grid of team members. Each card uses a native <details>
|
||||||
|
* element so the bio expands when the photo, name, or title is clicked.
|
||||||
|
*
|
||||||
|
* @package CWC
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace CWC;
|
||||||
|
|
||||||
|
$members = get_field( 'members' );
|
||||||
|
|
||||||
|
if ( empty( $members ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$classes = 'team-grid container my-section mx-auto';
|
||||||
|
$wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section <?php echo wp_kses_post( $wrapper ); ?>>
|
||||||
|
<div class="team-grid__inner grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-12">
|
||||||
|
<?php foreach ( $members as $member ) : ?>
|
||||||
|
<article class="team-grid__card">
|
||||||
|
<details class="team-grid__details">
|
||||||
|
<summary class="team-grid__summary">
|
||||||
|
<?php if ( ! empty( $member['image'] ) ) : ?>
|
||||||
|
<img
|
||||||
|
class="team-grid__photo"
|
||||||
|
src="<?php echo esc_url( $member['image']['url'] ); ?>"
|
||||||
|
alt="<?php echo esc_attr( $member['image']['alt'] ?? '' ); ?>"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty( $member['name'] ) ) : ?>
|
||||||
|
<h3 class="team-grid__name">
|
||||||
|
<?php echo wp_kses_post( $member['name'] ); ?>
|
||||||
|
</h3>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty( $member['title'] ) ) : ?>
|
||||||
|
<p class="team-grid__title">
|
||||||
|
<?php echo wp_kses_post( $member['title'] ); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<div class="team-grid__bio-wrap">
|
||||||
|
<div class="team-grid__bio-inner">
|
||||||
|
<?php if ( ! empty( $member['bio'] ) ) : ?>
|
||||||
|
<div class="team-grid__bio">
|
||||||
|
<?php echo wp_kses_post( $member['bio'] ); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Lint the PHP file with PHPCS**
|
||||||
|
|
||||||
|
Run: `composer exec phpcs -- views/blocks/team-grid/team-grid.php`
|
||||||
|
Expected: exits 0 with no errors. If PHPCS reports alignment warnings on the assignment block (similar to the Task 5 services-list fix), run `composer exec phpcbf -- views/blocks/team-grid/team-grid.php` to auto-fix, then re-run PHPCS to confirm exit 0. Whitespace-only changes are acceptable; do not modify logic.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify the dynamic utility classes are in the source**
|
||||||
|
|
||||||
|
Run: `grep -E "grid-cols-1|sm:grid-cols-2|lg:grid-cols-3|w-32|w-40|w-48" views/blocks/team-grid/team-grid.php`
|
||||||
|
Expected: at least 3 matches (Tailwind v4 must see the literal class names in the source to emit the rules).
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add views/blocks/team-grid/team-grid.php
|
||||||
|
git commit -m "✨ feat: Add team-grid block PHP template"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: CSS Import Wiring
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `styles/blocks/index.css`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `views/blocks/team-grid/team-grid.css` (from Task 3).
|
||||||
|
- Produces: A new `@import` line at the end of `styles/blocks/index.css` so Tailwind's build picks up the block's CSS.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add the import to the block index CSS**
|
||||||
|
|
||||||
|
Append the following line at the end of `styles/blocks/index.css`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import '../../views/blocks/team-grid/team-grid.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
After the edit, the file should end with:
|
||||||
|
|
||||||
|
```
|
||||||
|
@import '../../views/blocks/services-list/services-list.css';
|
||||||
|
@import '../../views/blocks/team-grid/team-grid.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the import is present**
|
||||||
|
|
||||||
|
Run: `grep -cF "team-grid/team-grid.css" styles/blocks/index.css`
|
||||||
|
Expected: `1`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add styles/blocks/index.css
|
||||||
|
git commit -m "🔧 chore: Import team-grid block CSS into theme"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: Build the CSS
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `static/dist/theme.css`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: The new import in `styles/blocks/index.css` (from Task 5) and the block CSS (from Task 3).
|
||||||
|
- Produces: A rebuilt `static/dist/theme.css` that includes the `.team-grid*` rules and the responsive `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3` utilities.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Run the production Tailwind build**
|
||||||
|
|
||||||
|
Run: `npm run build`
|
||||||
|
Expected: completes with no errors.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the team-grid CSS classes are in the built output**
|
||||||
|
|
||||||
|
Tailwind v4 unwraps CSS-nesting `&__name` into `__name.team-grid`. Use fixed-string search (Windows-safe):
|
||||||
|
|
||||||
|
Run: `grep -cF "__name.team-grid" static/dist/theme.css`
|
||||||
|
Expected: `1` or more (the rule is in the dist).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify the responsive grid utilities are in the built output**
|
||||||
|
|
||||||
|
Run: `grep -cF "sm\:grid-cols-2" static/dist/theme.css`
|
||||||
|
Expected: `1` or more.
|
||||||
|
|
||||||
|
Run: `grep -cF "lg\:grid-cols-3" static/dist/theme.css`
|
||||||
|
Expected: `1` or more.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit the rebuilt dist**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add -f static/dist/theme.css
|
||||||
|
git commit -m "🎨 build: Rebuild dist with team-grid block styles"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: Playwright Tests
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `tests/team-page.spec.js`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: The rendered `views/blocks/team-grid/team-grid.php` template (Tasks 3 + 4) on the WordPress page `/team/`.
|
||||||
|
- Produces: Three tests:
|
||||||
|
1. Photo, name, and title visible for the first card.
|
||||||
|
2. Bio is closed by default; clicking the summary opens the details and reveals the bio; clicking again closes it.
|
||||||
|
3. No axe-core a11y violations scoped to `.team-grid`.
|
||||||
|
|
||||||
|
> **Prerequisite:** the test depends on the Team page being composed in Gutenberg with at least one `Team Grid` block containing one or more members. That composition is the editor's responsibility and is not part of this plan. The tests should be authored against the intended state, not a placeholder state.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the test spec file**
|
||||||
|
|
||||||
|
Create the file `tests/team-page.spec.js` with the following exact content:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import AxeBuilder from "@axe-core/playwright";
|
||||||
|
|
||||||
|
test.describe("Team Grid block on /team/", () => {
|
||||||
|
test("renders the photo, name, and title for each member", async ({ page }) => {
|
||||||
|
await page.goto("/team/");
|
||||||
|
|
||||||
|
const cards = page.locator(".team-grid__card");
|
||||||
|
await expect(cards.first()).toBeVisible();
|
||||||
|
|
||||||
|
const firstCard = cards.first();
|
||||||
|
await expect(firstCard.locator(".team-grid__photo")).toBeVisible();
|
||||||
|
await expect(firstCard.locator(".team-grid__name")).toBeVisible();
|
||||||
|
await expect(firstCard.locator(".team-grid__title")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bio is hidden by default and expands on summary click", async ({ page }) => {
|
||||||
|
await page.goto("/team/");
|
||||||
|
|
||||||
|
const firstDetails = page.locator(".team-grid__details").first();
|
||||||
|
// Bio is closed by default.
|
||||||
|
await expect(firstDetails).not.toHaveAttribute("open", "");
|
||||||
|
|
||||||
|
// Click the summary; the details should open and the bio should be visible.
|
||||||
|
await firstDetails.locator(".team-grid__summary").click();
|
||||||
|
await expect(firstDetails).toHaveAttribute("open", "");
|
||||||
|
await expect(firstDetails.locator(".team-grid__bio")).toBeVisible();
|
||||||
|
|
||||||
|
// Click again; the details should close.
|
||||||
|
await firstDetails.locator(".team-grid__summary").click();
|
||||||
|
await expect(firstDetails).not.toHaveAttribute("open", "");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("no axe violations on the team-grid block", async ({ page }) => {
|
||||||
|
await page.goto("/team/");
|
||||||
|
const results = await new AxeBuilder({ page })
|
||||||
|
.include(".team-grid")
|
||||||
|
.analyze();
|
||||||
|
expect(results.violations).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run the tests**
|
||||||
|
|
||||||
|
Run: `npx playwright test tests/team-page.spec.js`
|
||||||
|
Expected: all 3 tests pass. If tests fail because the Team page has not yet been composed in the editor, that's an expected prerequisite — note the prerequisite in the final report and skip the test run.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
|
||||||
|
git add tests/team-page.spec.js
|
||||||
|
git commit -m "✅ test: Add Playwright tests for team-grid block"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Review Notes
|
||||||
|
|
||||||
|
- **Spec coverage:**
|
||||||
|
- One new ACF block (`team-grid`) — covered by Tasks 1, 2, 3, 4.
|
||||||
|
- Repeater of `members` with `image` / `name` / `title` / `bio` — covered by Task 1.
|
||||||
|
- Responsive grid `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3` — covered by Task 4 (PHP) and Task 6 (Tailwind emits the rules).
|
||||||
|
- Rounded-rectangle headshots via `rounded-md` — covered by Task 3 (CSS).
|
||||||
|
- Native `<details>`/`<summary>` click-to-expand — covered by Task 4 (PHP) and Task 3 (CSS).
|
||||||
|
- `grid-template-rows: 0fr → 1fr` animation — covered by Task 3 (CSS).
|
||||||
|
- No built-in background — explicitly out of scope (Section block wrapper, editor's responsibility).
|
||||||
|
- Page template not modified — confirmed; `page.php` is not in any file list.
|
||||||
|
- `header.php` / `footer.php` / page hero not touched — confirmed.
|
||||||
|
- Reuse existing design tokens (`text-cwc-blue-01`, `rounded-md`) — covered by Tasks 1, 3, 4.
|
||||||
|
- Playwright tests covering photo + name + title, summary click toggles, and a11y — covered by Task 7.
|
||||||
|
- Dist rebuild — covered by Task 6.
|
||||||
|
- **Placeholder scan:** No TBDs. Every step has a concrete file path, exact content, exact command, and expected output. The only conditional is in Task 7 Step 2 — explicitly called out as a prerequisite (editor must compose the page in Gutenberg), not a placeholder.
|
||||||
|
- **Type consistency:** ACF field `name: "members"` is used in Task 4 (`get_field( 'members' )`). Sub-field names `image`, `name`, `title`, `bio` are used in Task 1 (defined) and Task 4 (consumed). Class names `team-grid`, `__inner`, `__card`, `__details`, `__summary`, `__photo`, `__name`, `__title`, `__bio-wrap`, `__bio-inner`, `__bio` are used in Task 3 (defined in CSS) and Task 4 (rendered in markup) and Task 7 (asserted in tests) — consistent.
|
||||||
|
- **Ambiguity check:** "Rounded rectangle" is concretely specified as `rounded-md`. "Opens vertically" is concretely specified as `grid-template-rows: 0fr → 1fr` with 300ms transition. "Responsive grid" is concretely specified as `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3`. "No built-in background" is concretely scoped to the Section block wrapper (editor's responsibility, not this block's). All are explicit.
|
||||||
Reference in New Issue
Block a user