Skip to content

Commit 3b30b3e

Browse files
authored
feat(blocks): support heading 4/5/6 blocks (#73)
Add concrete block classes for Notion heading_4, heading_5, and heading_6 payloads so the existing heading hydration path preserves rich text for higher heading levels. Extend block tests to cover class resolution, serialization fields, and unsupported block fallback.
1 parent f473cbd commit 3b30b3e

5 files changed

Lines changed: 116 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## 1.9.0 - 2026-05-28
9+
10+
### Added
11+
12+
- Add support for Notion `heading_4`, `heading_5`, and `heading_6` blocks through `Heading4Block`, `Heading5Block`, and `Heading6Block`.
13+
814
## 1.8.3 - 2026-05-10
915

1016
### Fixed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brd6\NotionSdkPhp\Resource\Block;
6+
7+
use Brd6\NotionSdkPhp\Resource\Property\HeadingProperty;
8+
9+
class Heading4Block extends AbstractHeadingBlock
10+
{
11+
protected ?HeadingProperty $heading4 = null;
12+
13+
public function getHeading4(): ?HeadingProperty
14+
{
15+
return $this->heading4;
16+
}
17+
18+
public function setHeading4(?HeadingProperty $heading): self
19+
{
20+
$this->heading4 = $heading;
21+
22+
return $this;
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brd6\NotionSdkPhp\Resource\Block;
6+
7+
use Brd6\NotionSdkPhp\Resource\Property\HeadingProperty;
8+
9+
class Heading5Block extends AbstractHeadingBlock
10+
{
11+
protected ?HeadingProperty $heading5 = null;
12+
13+
public function getHeading5(): ?HeadingProperty
14+
{
15+
return $this->heading5;
16+
}
17+
18+
public function setHeading5(?HeadingProperty $heading): self
19+
{
20+
$this->heading5 = $heading;
21+
22+
return $this;
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brd6\NotionSdkPhp\Resource\Block;
6+
7+
use Brd6\NotionSdkPhp\Resource\Property\HeadingProperty;
8+
9+
class Heading6Block extends AbstractHeadingBlock
10+
{
11+
protected ?HeadingProperty $heading6 = null;
12+
13+
public function getHeading6(): ?HeadingProperty
14+
{
15+
return $this->heading6;
16+
}
17+
18+
public function setHeading6(?HeadingProperty $heading): self
19+
{
20+
$this->heading6 = $heading;
21+
22+
return $this;
23+
}
24+
}

tests/Resource/BlockTest.php

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@
1111
use Brd6\NotionSdkPhp\Resource\Block\ChildPageBlock;
1212
use Brd6\NotionSdkPhp\Resource\Block\ColumnBlock;
1313
use Brd6\NotionSdkPhp\Resource\Block\ColumnListBlock;
14+
use Brd6\NotionSdkPhp\Resource\Block\Heading1Block;
15+
use Brd6\NotionSdkPhp\Resource\Block\Heading2Block;
16+
use Brd6\NotionSdkPhp\Resource\Block\Heading3Block;
17+
use Brd6\NotionSdkPhp\Resource\Block\Heading4Block;
18+
use Brd6\NotionSdkPhp\Resource\Block\Heading5Block;
19+
use Brd6\NotionSdkPhp\Resource\Block\Heading6Block;
1420
use Brd6\NotionSdkPhp\Resource\Block\ParagraphBlock;
1521
use Brd6\NotionSdkPhp\Resource\Block\SyncedBlockBlock;
22+
use Brd6\NotionSdkPhp\Resource\Block\UnsupportedBlock;
1623
use Brd6\NotionSdkPhp\Resource\File\AbstractFile;
1724
use Brd6\NotionSdkPhp\Resource\File\Emoji;
1825
use Brd6\NotionSdkPhp\Resource\Property\CalloutProperty;
@@ -52,6 +59,17 @@ public function testInvalidBlockType(): void
5259
]);
5360
}
5461

62+
public function testUnsupportedBlock(): void
63+
{
64+
$block = AbstractBlock::fromRawData([
65+
'object' => 'block',
66+
'type' => 'unsupported_block',
67+
]);
68+
69+
$this->assertInstanceOf(UnsupportedBlock::class, $block);
70+
$this->assertEquals('unsupported_block', $block->getType());
71+
}
72+
5573
public function testBlock(): void
5674
{
5775
$block = AbstractBlock::fromRawData(
@@ -110,30 +128,42 @@ public function testParagraphBlock(): void
110128
public function testHeadingsBlock(): void
111129
{
112130
$headings = [
113-
'heading_1',
114-
'heading_2',
115-
'heading_3',
131+
'heading_1' => Heading1Block::class,
132+
'heading_2' => Heading2Block::class,
133+
'heading_3' => Heading3Block::class,
134+
'heading_4' => Heading4Block::class,
135+
'heading_5' => Heading5Block::class,
136+
'heading_6' => Heading6Block::class,
116137
];
117138

118-
foreach ($headings as $heading) {
139+
foreach ($headings as $heading => $blockClass) {
119140
$rawContent = (string) file_get_contents('tests/Fixtures/client_blocks_retrieve_block_heading1_200.json');
120141

121142
$rawContent = str_replace('heading_1', $heading, $rawContent);
143+
$rawData = (array) json_decode($rawContent, true);
122144

123145
$block = AbstractBlock::fromRawData(
124-
(array) json_decode(
125-
$rawContent,
126-
true,
127-
),
146+
$rawData,
128147
);
129148

149+
$blockData = $block->toArray();
130150
$typeFormatted = StringHelper::snakeCaseToCamelCase($block->getType());
131151
$getterMethodName = "get$typeFormatted";
132152

153+
$this->assertInstanceOf($blockClass, $block);
133154
$this->assertEquals($heading, $block->getType());
134155
$this->assertNotNull($block->$getterMethodName());
135156
$this->assertInstanceOf(HeadingProperty::class, $block->$getterMethodName());
136157
$this->assertGreaterThan(0, count($block->$getterMethodName()->getRichText()));
158+
$this->assertArrayHasKey($heading, $blockData);
159+
$this->assertSame($rawData[$heading]['color'], $blockData[$heading]['color']);
160+
$this->assertSame($rawData[$heading]['rich_text'][0]['type'], $blockData[$heading]['rich_text'][0]['type']);
161+
$this->assertEquals($rawData[$heading]['rich_text'][0]['annotations'], $blockData[$heading]['rich_text'][0]['annotations']);
162+
$this->assertSame($rawData[$heading]['rich_text'][0]['plain_text'], $blockData[$heading]['rich_text'][0]['plain_text']);
163+
$this->assertSame(
164+
$rawData[$heading]['rich_text'][0]['text']['content'],
165+
$blockData[$heading]['rich_text'][0]['text']['content'],
166+
);
137167

138168
$richText = $block->$getterMethodName()->getRichText()[0];
139169

0 commit comments

Comments
 (0)